pull/5370/head
aniket-engg 1 year ago
parent 6014b9522c
commit fc3431d42a
  1. 433
      libs/remix-ui/editor/src/lib/remix-ui-editor.tsx

@ -3,9 +3,15 @@ import {isArray} from 'lodash'
import Editor, {loader, Monaco} from '@monaco-editor/react' import Editor, {loader, Monaco} from '@monaco-editor/react'
import {AlertModal} from '@remix-ui/app' import {AlertModal} from '@remix-ui/app'
import {reducerActions, reducerListener, initialState} from './actions/editor' import {reducerActions, reducerListener, initialState} from './actions/editor'
import {solidityTokensProvider, solidityLanguageConfig} from './syntaxes/solidity' import {
solidityTokensProvider,
solidityLanguageConfig
} from './syntaxes/solidity'
import {cairoTokensProvider, cairoLanguageConfig} from './syntaxes/cairo' import {cairoTokensProvider, cairoLanguageConfig} from './syntaxes/cairo'
import {zokratesTokensProvider, zokratesLanguageConfig} from './syntaxes/zokrates' import {
zokratesTokensProvider,
zokratesLanguageConfig
} from './syntaxes/zokrates'
import {moveTokenProvider, moveLanguageConfig} from './syntaxes/move' import {moveTokenProvider, moveLanguageConfig} from './syntaxes/move'
import {monacoTypes} from '@remix-ui/editor' import {monacoTypes} from '@remix-ui/editor'
import {loadTypes} from './web-types' import {loadTypes} from './web-types'
@ -95,7 +101,14 @@ export type DecorationsReturn = {
export type PluginType = { export type PluginType = {
on: (plugin: string, event: string, listener: any) => void on: (plugin: string, event: string, listener: any) => void
call: (plugin: string, method: string, arg1?: any, arg2?: any, arg3?: any, arg4?: any) => any call: (
plugin: string,
method: string,
arg1?: any,
arg2?: any,
arg3?: any,
arg4?: any
) => any
} }
export type EditorAPIType = { export type EditorAPIType = {
@ -104,12 +117,30 @@ export type EditorAPIType = {
getValue: (uri: string) => string getValue: (uri: string) => string
getCursorPosition: (offset?: boolean) => number | monacoTypes.IPosition getCursorPosition: (offset?: boolean) => number | monacoTypes.IPosition
getHoverPosition: (position: monacoTypes.IPosition) => number getHoverPosition: (position: monacoTypes.IPosition) => number
addDecoration: (marker: sourceMarker, filePath: string, typeOfDecoration: string) => DecorationsReturn addDecoration: (
clearDecorationsByPlugin: (filePath: string, plugin: string, typeOfDecoration: string, registeredDecorations: any, currentDecorations: any) => DecorationsReturn marker: sourceMarker,
keepDecorationsFor: (filePath: string, plugin: string, typeOfDecoration: string, registeredDecorations: any, currentDecorations: any) => DecorationsReturn 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: errorMarker[], from: string) => void addErrorMarker: (errors: errorMarker[], from: string) => void
clearErrorMarkers: (sources: string[] | {[fileName: string]: any}, from: string) => void clearErrorMarkers: (
getPositionAt: (offset: number) => monacoTypes.IPosition sources: string[] | {[fileName: string]: any},
from: string
) => void
} }
/* eslint-disable-next-line */ /* eslint-disable-next-line */
@ -161,7 +192,10 @@ export const EditorUI = (props: EditorUIProps) => {
const [editorModelsState, dispatch] = useReducer(reducerActions, initialState) const [editorModelsState, dispatch] = useReducer(reducerActions, initialState)
const formatColor = (name) => { const formatColor = (name) => {
let color = window.getComputedStyle(document.documentElement).getPropertyValue(name).trim() let color = window
.getComputedStyle(document.documentElement)
.getPropertyValue(name)
.trim()
if (color.length === 4) { if (color.length === 4) {
color = color.concat(color.substr(1)) color = color.concat(color.substr(1))
} }
@ -287,7 +321,8 @@ export const EditorUI = (props: EditorUIProps) => {
'editorSuggestWidget.highlightForeground': primaryColor, 'editorSuggestWidget.highlightForeground': primaryColor,
'editorSuggestWidget.focusHighlightForeground': infoColor, 'editorSuggestWidget.focusHighlightForeground': infoColor,
'editor.lineHighlightBorder': secondaryColor, 'editor.lineHighlightBorder': secondaryColor,
'editor.lineHighlightBackground': textbackground === darkColor ? lightColor : secondaryColor, 'editor.lineHighlightBackground':
textbackground === darkColor ? lightColor : secondaryColor,
'editorGutter.background': lightColor, 'editorGutter.background': lightColor,
//'editor.selectionHighlightBackground': secondaryColor, //'editor.selectionHighlightBackground': secondaryColor,
'minimap.background': lightColor, 'minimap.background': lightColor,
@ -309,7 +344,9 @@ export const EditorUI = (props: EditorUIProps) => {
useEffect(() => { useEffect(() => {
if (!editorRef.current || !props.currentFile) return if (!editorRef.current || !props.currentFile) return
currentFileRef.current = props.currentFile currentFileRef.current = props.currentFile
props.plugin.call('fileManager', 'getUrlFromPath', currentFileRef.current).then((url) => (currentUrlRef.current = url.file)) props.plugin
.call('fileManager', 'getUrlFromPath', currentFileRef.current)
.then((url) => (currentUrlRef.current = url.file))
const file = editorModelsState[props.currentFile] const file = editorModelsState[props.currentFile]
editorRef.current.setModel(file.model) editorRef.current.setModel(file.model)
@ -329,18 +366,34 @@ export const EditorUI = (props: EditorUIProps) => {
} }
}, [props.currentFile]) }, [props.currentFile])
const convertToMonacoDecoration = (decoration: lineText | sourceAnnotation | sourceMarker, typeOfDecoration: string) => { const convertToMonacoDecoration = (
decoration: lineText | sourceAnnotation | sourceMarker,
typeOfDecoration: string
) => {
if (typeOfDecoration === 'sourceAnnotationsPerFile') { if (typeOfDecoration === 'sourceAnnotationsPerFile') {
decoration = decoration as sourceAnnotation decoration = decoration as sourceAnnotation
return { return {
type: typeOfDecoration, type: typeOfDecoration,
range: new monacoRef.current.Range(decoration.row + 1, 1, decoration.row + 1, 1), range: new monacoRef.current.Range(
decoration.row + 1,
1,
decoration.row + 1,
1
),
options: { options: {
isWholeLine: false, isWholeLine: false,
glyphMarginHoverMessage: { glyphMarginHoverMessage: {
value: (decoration.from ? `from ${decoration.from}:\n` : '') + decoration.text value:
(decoration.from ? `from ${decoration.from}:\n` : '') +
decoration.text
}, },
glyphMarginClassName: `fal fa-exclamation-square text-${decoration.type === 'error' ? 'danger' : decoration.type === 'warning' ? 'warning' : 'info'}` glyphMarginClassName: `fal fa-exclamation-square text-${
decoration.type === 'error'
? 'danger'
: decoration.type === 'warning'
? 'warning'
: 'info'
}`
} }
} }
} }
@ -348,7 +401,9 @@ export const EditorUI = (props: EditorUIProps) => {
decoration = decoration as sourceMarker decoration = decoration as sourceMarker
let isWholeLine = false let isWholeLine = false
if ( if (
(decoration.position.start.line === decoration.position.end.line && decoration.position.end.column - decoration.position.start.column < 2) || (decoration.position.start.line === decoration.position.end.line &&
decoration.position.end.column - decoration.position.start.column <
2) ||
decoration.position.start.line !== decoration.position.end.line decoration.position.start.line !== decoration.position.end.line
) { ) {
// in this case we force highlighting the whole line (doesn't make sense to highlight 2 chars) // in this case we force highlighting the whole line (doesn't make sense to highlight 2 chars)
@ -364,7 +419,9 @@ export const EditorUI = (props: EditorUIProps) => {
), ),
options: { options: {
isWholeLine, isWholeLine,
inlineClassName: `${isWholeLine ? 'alert-info' : 'inline-class'} border-0 highlightLine${decoration.position.start.line + 1}` inlineClassName: `${
isWholeLine ? 'alert-info' : 'inline-class'
} border-0 highlightLine${decoration.position.start.line + 1}`
} }
} }
} }
@ -410,7 +467,13 @@ export const EditorUI = (props: EditorUIProps) => {
} }
} }
props.editorAPI.clearDecorationsByPlugin = (filePath: string, plugin: string, typeOfDecoration: string, registeredDecorations: any, currentDecorations: any) => { props.editorAPI.clearDecorationsByPlugin = (
filePath: string,
plugin: string,
typeOfDecoration: string,
registeredDecorations: any,
currentDecorations: any
) => {
const model = editorModelsState[filePath]?.model const model = editorModelsState[filePath]?.model
if (!model) if (!model)
return { return {
@ -421,19 +484,33 @@ export const EditorUI = (props: EditorUIProps) => {
const newRegisteredDecorations = [] const newRegisteredDecorations = []
if (registeredDecorations) { if (registeredDecorations) {
for (const decoration of registeredDecorations) { for (const decoration of registeredDecorations) {
if (decoration.type === typeOfDecoration && decoration.value.from !== plugin) { if (
decorations.push(convertToMonacoDecoration(decoration.value, typeOfDecoration)) decoration.type === typeOfDecoration &&
decoration.value.from !== plugin
) {
decorations.push(
convertToMonacoDecoration(decoration.value, typeOfDecoration)
)
newRegisteredDecorations.push(decoration) newRegisteredDecorations.push(decoration)
} }
} }
} }
return { return {
currentDecorations: model.deltaDecorations(currentDecorations, decorations), currentDecorations: model.deltaDecorations(
currentDecorations,
decorations
),
registeredDecorations: newRegisteredDecorations registeredDecorations: newRegisteredDecorations
} }
} }
props.editorAPI.keepDecorationsFor = (filePath: string, plugin: string, typeOfDecoration: string, registeredDecorations: any, currentDecorations: any) => { props.editorAPI.keepDecorationsFor = (
filePath: string,
plugin: string,
typeOfDecoration: string,
registeredDecorations: any,
currentDecorations: any
) => {
const model = editorModelsState[filePath]?.model const model = editorModelsState[filePath]?.model
if (!model) if (!model)
return { return {
@ -443,37 +520,63 @@ export const EditorUI = (props: EditorUIProps) => {
if (registeredDecorations) { if (registeredDecorations) {
for (const decoration of registeredDecorations) { for (const decoration of registeredDecorations) {
if (decoration.value.from === plugin) { if (decoration.value.from === plugin) {
decorations.push(convertToMonacoDecoration(decoration.value, typeOfDecoration)) decorations.push(
convertToMonacoDecoration(decoration.value, typeOfDecoration)
)
} }
} }
} }
return { return {
currentDecorations: model.deltaDecorations(currentDecorations, decorations) currentDecorations: model.deltaDecorations(
currentDecorations,
decorations
)
} }
} }
const addDecoration = (decoration: sourceAnnotation | sourceMarker, filePath: string, typeOfDecoration: string) => { const addDecoration = (
decoration: sourceAnnotation | sourceMarker,
filePath: string,
typeOfDecoration: string
) => {
const model = editorModelsState[filePath]?.model const model = editorModelsState[filePath]?.model
if (!model) return {currentDecorations: []} if (!model) return {currentDecorations: []}
const monacoDecoration = convertToMonacoDecoration(decoration, typeOfDecoration) const monacoDecoration = convertToMonacoDecoration(
decoration,
typeOfDecoration
)
return { return {
currentDecorations: model.deltaDecorations([], [monacoDecoration]), currentDecorations: model.deltaDecorations([], [monacoDecoration]),
registeredDecorations: [{value: decoration, type: typeOfDecoration}] registeredDecorations: [{value: decoration, type: typeOfDecoration}]
} }
} }
props.editorAPI.addDecoration = (marker: sourceMarker, filePath: string, typeOfDecoration: string) => { props.editorAPI.addDecoration = (
marker: sourceMarker,
filePath: string,
typeOfDecoration: string
) => {
return addDecoration(marker, filePath, typeOfDecoration) return addDecoration(marker, filePath, typeOfDecoration)
} }
props.editorAPI.addErrorMarker = async (errors: errorMarker[], from: string) => { props.editorAPI.addErrorMarker = async (
const allMarkersPerfile: Record<string, Array<monacoTypes.editor.IMarkerData>> = {} errors: errorMarker[],
from: string
) => {
const allMarkersPerfile: Record<
string,
Array<monacoTypes.editor.IMarkerData>
> = {}
for (const error of errors) { for (const error of errors) {
let filePath = error.file let filePath = error.file
if (!filePath) return if (!filePath) return
const fileFromUrl = await props.plugin.call('fileManager', 'getPathFromUrl', filePath) const fileFromUrl = await props.plugin.call(
'fileManager',
'getPathFromUrl',
filePath
)
filePath = fileFromUrl.file filePath = fileFromUrl.file
const model = editorModelsState[filePath]?.model const model = editorModelsState[filePath]?.model
const errorServerityMap = { const errorServerityMap = {
@ -483,9 +586,14 @@ export const EditorUI = (props: EditorUIProps) => {
} }
if (model) { if (model) {
const markerData: monacoTypes.editor.IMarkerData = { const markerData: monacoTypes.editor.IMarkerData = {
severity: typeof error.severity === 'string' ? errorServerityMap[error.severity] : error.severity, severity:
startLineNumber: (error.position.start && error.position.start.line) || 0, typeof error.severity === 'string'
startColumn: (error.position.start && error.position.start.column) || 0, ? errorServerityMap[error.severity]
: 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, endLineNumber: (error.position.end && error.position.end.line) || 0,
endColumn: (error.position.end && error.position.end.column) || 0, endColumn: (error.position.end && error.position.end.column) || 0,
message: error.message message: error.message
@ -499,14 +607,23 @@ export const EditorUI = (props: EditorUIProps) => {
for (const filePath in allMarkersPerfile) { for (const filePath in allMarkersPerfile) {
const model = editorModelsState[filePath]?.model const model = editorModelsState[filePath]?.model
if (model) { if (model) {
monacoRef.current.editor.setModelMarkers(model, from, allMarkersPerfile[filePath]) monacoRef.current.editor.setModelMarkers(
model,
from,
allMarkersPerfile[filePath]
)
} }
} }
} }
props.editorAPI.clearErrorMarkers = async (sources: string[] | {[fileName: string]: any}, from: string) => { props.editorAPI.clearErrorMarkers = async (
sources: string[] | {[fileName: string]: any},
from: string
) => {
if (sources) { if (sources) {
for (const source of Array.isArray(sources) ? sources : Object.keys(sources)) { for (const source of Array.isArray(sources)
? sources
: Object.keys(sources)) {
const filePath = source const filePath = source
const model = editorModelsState[filePath]?.model const model = editorModelsState[filePath]?.model
if (model) { if (model) {
@ -534,7 +651,9 @@ export const EditorUI = (props: EditorUIProps) => {
if (!monacoRef.current) return if (!monacoRef.current) return
const model = editorModelsState[currentFileRef.current]?.model const model = editorModelsState[currentFileRef.current]?.model
if (model) { if (model) {
return offset ? model.getOffsetAt(editorRef.current.getPosition()) : editorRef.current.getPosition() return offset
? model.getOffsetAt(editorRef.current.getPosition())
: editorRef.current.getPosition()
} }
} }
@ -550,8 +669,9 @@ export const EditorUI = (props: EditorUIProps) => {
props.editorAPI.getFontSize = () => { props.editorAPI.getFontSize = () => {
if (!editorRef.current) return if (!editorRef.current) return
return editorRef.current.getOption(43).fontSize return editorRef.current.getOption(51)
} }
props.editorAPI.getPositionAt = (offset: number): IPosition => { props.editorAPI.getPositionAt = (offset: number): IPosition => {
return editorRef.current.getModel().getPositionAt(offset) return editorRef.current.getModel().getPositionAt(offset)
} }
@ -562,10 +682,15 @@ export const EditorUI = (props: EditorUIProps) => {
setCurrentBreakpoints((prevState) => { setCurrentBreakpoints((prevState) => {
const currentFile = currentUrlRef.current const currentFile = currentUrlRef.current
if (!prevState[currentFile]) prevState[currentFile] = {} if (!prevState[currentFile]) prevState[currentFile] = {}
const decoration = Object.keys(prevState[currentFile]).filter((line) => parseInt(line) === position.lineNumber) const decoration = Object.keys(prevState[currentFile]).filter(
(line) => parseInt(line) === position.lineNumber
)
if (decoration.length) { if (decoration.length) {
props.events.onBreakPointCleared(currentFile, position.lineNumber) props.events.onBreakPointCleared(currentFile, position.lineNumber)
model.deltaDecorations([prevState[currentFile][position.lineNumber]], []) model.deltaDecorations(
[prevState[currentFile][position.lineNumber]],
[]
)
delete prevState[currentFile][position.lineNumber] delete prevState[currentFile][position.lineNumber]
} else { } else {
props.events.onBreakPointAdded(currentFile, position.lineNumber) props.events.onBreakPointAdded(currentFile, position.lineNumber)
@ -573,7 +698,12 @@ export const EditorUI = (props: EditorUIProps) => {
[], [],
[ [
{ {
range: new monacoRef.current.Range(position.lineNumber, 1, position.lineNumber, 1), range: new monacoRef.current.Range(
position.lineNumber,
1,
position.lineNumber,
1
),
options: { options: {
isWholeLine: false, isWholeLine: false,
glyphMarginClassName: 'fas fa-circle text-info' glyphMarginClassName: 'fas fa-circle text-info'
@ -591,7 +721,13 @@ export const EditorUI = (props: EditorUIProps) => {
function handleEditorDidMount(editor) { function handleEditorDidMount(editor) {
editorRef.current = editor editorRef.current = editor
defineAndSetTheme(monacoRef.current) defineAndSetTheme(monacoRef.current)
reducerListener(props.plugin, dispatch, monacoRef.current, editorRef.current, props.events) reducerListener(
props.plugin,
dispatch,
monacoRef.current,
editorRef.current,
props.events
)
props.events.onEditorMounted() props.events.onEditorMounted()
editor.onMouseUp((e) => { editor.onMouseUp((e) => {
// see https://microsoft.github.io/monaco-editor/typedoc/enums/editor.MouseTargetType.html // see https://microsoft.github.io/monaco-editor/typedoc/enums/editor.MouseTargetType.html
@ -603,7 +739,14 @@ export const EditorUI = (props: EditorUIProps) => {
}) })
editor.onDidPaste((e) => { editor.onDidPaste((e) => {
if (!pasteCodeRef.current && e && e.range && e.range.startLineNumber >= 0 && e.range.endLineNumber >= 0 && e.range.endLineNumber - e.range.startLineNumber > 10) { if (
!pasteCodeRef.current &&
e &&
e.range &&
e.range.startLineNumber >= 0 &&
e.range.endLineNumber >= 0 &&
e.range.endLineNumber - e.range.startLineNumber > 10
) {
const modalContent: AlertModal = { const modalContent: AlertModal = {
id: 'newCodePasted', id: 'newCodePasted',
title: 'Pasted Code Alert', title: 'Pasted Code Alert',
@ -613,16 +756,28 @@ export const EditorUI = (props: EditorUIProps) => {
<i className="fas fa-exclamation-triangle text-danger mr-1"></i> <i className="fas fa-exclamation-triangle text-danger mr-1"></i>
You have just pasted a code snippet or contract in the editor. You have just pasted a code snippet or contract in the editor.
<div> <div>
Make sure you fully understand this code before deploying or interacting with it. Don't get scammed! Make sure you fully understand this code before deploying or
interacting with it. Don't get scammed!
<div className="mt-2"> <div className="mt-2">
Running untrusted code can put your wallet <span className="text-warning"> at risk </span>. In a worst-case scenario, you could{' '} Running untrusted code can put your wallet{' '}
<span className="text-warning"> at risk </span>. In a
worst-case scenario, you could{' '}
<span className="text-warning">lose all your money</span>. <span className="text-warning">lose all your money</span>.
</div> </div>
<div className="text-warning mt-2">If you don't fully understand it, please don't run this code.</div> <div className="text-warning mt-2">
<div className="mt-2">If you are not a smart contract developer, ask someone you trust who has the skills to determine if this code is safe to use.</div> If you don't fully understand it, please don't run this code.
</div>
<div className="mt-2">
If you are not a smart contract developer, ask someone you
trust who has the skills to determine if this code is safe to
use.
</div>
<div className="mt-2"> <div className="mt-2">
See{' '} See{' '}
<a target="_blank" href="https://remix-ide.readthedocs.io/en/latest/security.html"> <a
target="_blank"
href="https://remix-ide.readthedocs.io/en/latest/security.html"
>
{' '} {' '}
these recommendations{' '} these recommendations{' '}
</a>{' '} </a>{' '}
@ -638,12 +793,20 @@ export const EditorUI = (props: EditorUIProps) => {
}) })
// zoomin zoomout // zoomin zoomout
editor.addCommand(monacoRef.current.KeyMod.CtrlCmd | (monacoRef.current.KeyCode as any).US_EQUAL, () => { editor.addCommand(
editor.updateOptions({fontSize: editor.getOption(43).fontSize + 1}) monacoRef.current.KeyMod.CtrlCmd |
}) (monacoRef.current.KeyCode as any).US_EQUAL,
editor.addCommand(monacoRef.current.KeyMod.CtrlCmd | (monacoRef.current.KeyCode as any).US_MINUS, () => { () => {
editor.updateOptions({fontSize: editor.getOption(43).fontSize - 1}) editor.updateOptions({fontSize: editor.getOption(51) + 1})
}) }
)
editor.addCommand(
monacoRef.current.KeyMod.CtrlCmd |
(monacoRef.current.KeyCode as any).US_MINUS,
() => {
editor.updateOptions({fontSize: editor.getOption(51) - 1})
}
)
// add context menu items // add context menu items
const zoominAction = { const zoominAction = {
@ -656,7 +819,7 @@ export const EditorUI = (props: EditorUIProps) => {
monacoRef.current.KeyMod.CtrlCmd | monacoRef.current.KeyCode.Equal monacoRef.current.KeyMod.CtrlCmd | monacoRef.current.KeyCode.Equal
], ],
run: () => { run: () => {
editor.updateOptions({fontSize: editor.getOption(43).fontSize + 1}) editor.updateOptions({fontSize: editor.getOption(51) + 1})
} }
} }
const zoomOutAction = { const zoomOutAction = {
@ -669,7 +832,7 @@ export const EditorUI = (props: EditorUIProps) => {
monacoRef.current.KeyMod.CtrlCmd | monacoRef.current.KeyCode.Minus monacoRef.current.KeyMod.CtrlCmd | monacoRef.current.KeyCode.Minus
], ],
run: () => { run: () => {
editor.updateOptions({fontSize: editor.getOption(43).fontSize - 1}) editor.updateOptions({fontSize: editor.getOption(51) - 1})
} }
} }
const formatAction = { const formatAction = {
@ -679,7 +842,9 @@ export const EditorUI = (props: EditorUIProps) => {
contextMenuGroupId: 'formatting', // create a new grouping contextMenuGroupId: 'formatting', // create a new grouping
keybindings: [ keybindings: [
// eslint-disable-next-line no-bitwise // eslint-disable-next-line no-bitwise
monacoRef.current.KeyMod.Shift | monacoRef.current.KeyMod.Alt | monacoRef.current.KeyCode.KeyF monacoRef.current.KeyMod.Shift |
monacoRef.current.KeyMod.Alt |
monacoRef.current.KeyCode.KeyF
], ],
run: async () => { run: async () => {
const file = await props.plugin.call('fileManager', 'getCurrentFile') const file = await props.plugin.call('fileManager', 'getCurrentFile')
@ -687,7 +852,10 @@ export const EditorUI = (props: EditorUIProps) => {
} }
} }
const freeFunctionCondition = editor.createContextKey('freeFunctionCondition', false) const freeFunctionCondition = editor.createContextKey(
'freeFunctionCondition',
false
)
let freeFunctionAction let freeFunctionAction
const executeFreeFunctionAction = { const executeFreeFunctionAction = {
id: 'executeFreeFunction', id: 'executeFreeFunction',
@ -697,21 +865,48 @@ export const EditorUI = (props: EditorUIProps) => {
precondition: 'freeFunctionCondition', precondition: 'freeFunctionCondition',
keybindings: [ keybindings: [
// eslint-disable-next-line no-bitwise // eslint-disable-next-line no-bitwise
monacoRef.current.KeyMod.Shift | monacoRef.current.KeyMod.Alt | monacoRef.current.KeyCode.KeyR monacoRef.current.KeyMod.Shift |
monacoRef.current.KeyMod.Alt |
monacoRef.current.KeyCode.KeyR
], ],
run: async () => { run: async () => {
const {nodesAtPosition} = await retrieveNodesAtPosition(props.editorAPI, props.plugin) const {nodesAtPosition} = await retrieveNodesAtPosition(
props.editorAPI,
props.plugin
)
// find the contract and get the nodes of the contract and the base contracts and imports // find the contract and get the nodes of the contract and the base contracts and imports
if (nodesAtPosition && isArray(nodesAtPosition) && nodesAtPosition.length) { if (
const freeFunctionNode = nodesAtPosition.find((node) => node.kind === 'freeFunction') nodesAtPosition &&
isArray(nodesAtPosition) &&
nodesAtPosition.length
) {
const freeFunctionNode = nodesAtPosition.find(
(node) => node.kind === 'freeFunction'
)
if (freeFunctionNode) { if (freeFunctionNode) {
const file = await props.plugin.call('fileManager', 'getCurrentFile') const file = await props.plugin.call(
props.plugin.call('solidity-script', 'execute', file, freeFunctionNode.name) 'fileManager',
'getCurrentFile'
)
props.plugin.call(
'solidity-script',
'execute',
file,
freeFunctionNode.name
)
} else { } else {
props.plugin.call('notification', 'toast', 'This can only execute free function') props.plugin.call(
'notification',
'toast',
'This can only execute free function'
)
} }
} else { } else {
props.plugin.call('notification', 'toast', 'Please go to Remix settings and activate the code editor features or wait that the current editor context is loaded.') props.plugin.call(
'notification',
'toast',
'Please go to Remix settings and activate the code editor features or wait that the current editor context is loaded.'
)
} }
} }
} }
@ -721,7 +916,12 @@ export const EditorUI = (props: EditorUIProps) => {
freeFunctionAction = editor.addAction(executeFreeFunctionAction) freeFunctionAction = editor.addAction(executeFreeFunctionAction)
// we have to add the command because the menu action isn't always available (see onContextMenuHandlerForFreeFunction) // we have to add the command because the menu action isn't always available (see onContextMenuHandlerForFreeFunction)
editor.addCommand(monacoRef.current.KeyMod.Shift | monacoRef.current.KeyMod.Alt | monacoRef.current.KeyCode.KeyR, () => executeFreeFunctionAction.run()) editor.addCommand(
monacoRef.current.KeyMod.Shift |
monacoRef.current.KeyMod.Alt |
monacoRef.current.KeyCode.KeyR,
() => executeFreeFunctionAction.run()
)
const contextmenu = editor.getContribution('editor.contrib.contextmenu') const contextmenu = editor.getContribution('editor.contrib.contextmenu')
const orgContextMenuMethod = contextmenu._onContextMenu const orgContextMenuMethod = contextmenu._onContextMenu
@ -735,8 +935,13 @@ export const EditorUI = (props: EditorUIProps) => {
freeFunctionCondition.set(false) freeFunctionCondition.set(false)
return return
} }
const {nodesAtPosition} = await retrieveNodesAtPosition(props.editorAPI, props.plugin) const {nodesAtPosition} = await retrieveNodesAtPosition(
const freeFunctionNode = nodesAtPosition.find((node) => node.kind === 'freeFunction') props.editorAPI,
props.plugin
)
const freeFunctionNode = nodesAtPosition.find(
(node) => node.kind === 'freeFunction'
)
if (freeFunctionNode) { if (freeFunctionNode) {
executeFreeFunctionAction.label = `Run the free function "${freeFunctionNode.name}" in the Remix VM` executeFreeFunctionAction.label = `Run the free function "${freeFunctionNode.name}" in the Remix VM`
freeFunctionAction = editor.addAction(executeFreeFunctionAction) freeFunctionAction = editor.addAction(executeFreeFunctionAction)
@ -786,27 +991,75 @@ export const EditorUI = (props: EditorUIProps) => {
monacoRef.current.languages.register({id: 'remix-circom'}) monacoRef.current.languages.register({id: 'remix-circom'})
// Register a tokens provider for the language // Register a tokens provider for the language
monacoRef.current.languages.setMonarchTokensProvider('remix-solidity', solidityTokensProvider as any) monacoRef.current.languages.setMonarchTokensProvider(
monacoRef.current.languages.setLanguageConfiguration('remix-solidity', solidityLanguageConfig as any) 'remix-solidity',
solidityTokensProvider as any
)
monacoRef.current.languages.setLanguageConfiguration(
'remix-solidity',
solidityLanguageConfig as any
)
monacoRef.current.languages.setMonarchTokensProvider('remix-cairo', cairoTokensProvider as any) monacoRef.current.languages.setMonarchTokensProvider(
monacoRef.current.languages.setLanguageConfiguration('remix-cairo', cairoLanguageConfig as any) 'remix-cairo',
cairoTokensProvider as any
)
monacoRef.current.languages.setLanguageConfiguration(
'remix-cairo',
cairoLanguageConfig as any
)
monacoRef.current.languages.setMonarchTokensProvider('remix-zokrates', zokratesTokensProvider as any) monacoRef.current.languages.setMonarchTokensProvider(
monacoRef.current.languages.setLanguageConfiguration('remix-zokrates', zokratesLanguageConfig as any) 'remix-zokrates',
zokratesTokensProvider as any
)
monacoRef.current.languages.setLanguageConfiguration(
'remix-zokrates',
zokratesLanguageConfig as any
)
monacoRef.current.languages.setMonarchTokensProvider('remix-move', moveTokenProvider as any) monacoRef.current.languages.setMonarchTokensProvider(
monacoRef.current.languages.setLanguageConfiguration('remix-move', moveLanguageConfig as any) 'remix-move',
moveTokenProvider as any
)
monacoRef.current.languages.setLanguageConfiguration(
'remix-move',
moveLanguageConfig as any
)
monacoRef.current.languages.setMonarchTokensProvider('remix-circom', circomTokensProvider as any) monacoRef.current.languages.setMonarchTokensProvider(
monacoRef.current.languages.setLanguageConfiguration('remix-circom', circomLanguageConfig(monacoRef.current) as any) 'remix-circom',
circomTokensProvider as any
)
monacoRef.current.languages.setLanguageConfiguration(
'remix-circom',
circomLanguageConfig(monacoRef.current) as any
)
monacoRef.current.languages.registerDefinitionProvider('remix-solidity', new RemixDefinitionProvider(props, monaco)) monacoRef.current.languages.registerDefinitionProvider(
monacoRef.current.languages.registerDocumentHighlightProvider('remix-solidity', new RemixHighLightProvider(props, monaco)) 'remix-solidity',
monacoRef.current.languages.registerReferenceProvider('remix-solidity', new RemixReferenceProvider(props, monaco)) new RemixDefinitionProvider(props, monaco)
monacoRef.current.languages.registerHoverProvider('remix-solidity', new RemixHoverProvider(props, monaco)) )
monacoRef.current.languages.registerCompletionItemProvider('remix-solidity', new RemixCompletionProvider(props, monaco)) monacoRef.current.languages.registerDocumentHighlightProvider(
monaco.languages.registerCodeActionProvider('remix-solidity', new RemixCodeActionProvider(props, monaco)) 'remix-solidity',
new RemixHighLightProvider(props, monaco)
)
monacoRef.current.languages.registerReferenceProvider(
'remix-solidity',
new RemixReferenceProvider(props, monaco)
)
monacoRef.current.languages.registerHoverProvider(
'remix-solidity',
new RemixHoverProvider(props, monaco)
)
monacoRef.current.languages.registerCompletionItemProvider(
'remix-solidity',
new RemixCompletionProvider(props, monaco)
)
monaco.languages.registerCodeActionProvider(
'remix-solidity',
new RemixCodeActionProvider(props, monaco)
)
loadTypes(monacoRef.current) loadTypes(monacoRef.current)
} }
@ -816,12 +1069,18 @@ export const EditorUI = (props: EditorUIProps) => {
<Editor <Editor
width="100%" width="100%"
path={props.currentFile} path={props.currentFile}
language={editorModelsState[props.currentFile] ? editorModelsState[props.currentFile].language : 'text'} language={
editorModelsState[props.currentFile]
? editorModelsState[props.currentFile].language
: 'text'
}
onMount={handleEditorDidMount} onMount={handleEditorDidMount}
beforeMount={handleEditorWillMount} beforeMount={handleEditorWillMount}
options={{ options={{
glyphMargin: true, glyphMargin: true,
readOnly: (!editorRef.current || !props.currentFile) && editorModelsState[props.currentFile]?.readOnly readOnly:
(!editorRef.current || !props.currentFile) &&
editorModelsState[props.currentFile]?.readOnly
}} }}
defaultValue={defaultEditorValue} defaultValue={defaultEditorValue}
/> />

Loading…
Cancel
Save