@ -24,7 +24,7 @@ enum MarkerSeverity {
Hint = 1 ,
Info = 2 ,
Warning = 4 ,
Error = 8
Error = 8 ,
}
type sourceAnnotation = {
@ -109,6 +109,7 @@ export type EditorAPIType = {
keepDecorationsFor : ( filePath : string , plugin : string , typeOfDecoration : string , registeredDecorations : any , currentDecorations : any ) = > DecorationsReturn
addErrorMarker : ( errors : errorMarker [ ] , from : string ) = > void
clearErrorMarkers : ( sources : string [ ] | { [ fileName : string ] : any } , from : string ) = > void
getPositionAt : ( offset : number ) = > monacoTypes . IPosition
}
/* eslint-disable-next-line */
@ -152,6 +153,7 @@ export const EditorUI = (props: EditorUIProps) => {
const pasteCodeRef = useRef ( false )
const editorRef = useRef ( null )
const monacoRef = useRef < Monaco > ( null )
const currentFunction = useRef ( '' )
const currentFileRef = useRef ( '' )
const currentUrlRef = useRef ( '' )
// const currentDecorations = useRef({ sourceAnnotationsPerFile: {}, markerPerFile: {} }) // decorations that are currently in use by the editor
@ -275,7 +277,7 @@ export const EditorUI = (props: EditorUIProps) => {
// returns
{ token : 'keyword.returns' , foreground : greenColor } ,
{ token : 'keyword.return' , foreground : greenColor }
{ token : 'keyword.return' , foreground : greenColor } ,
] ,
colors : {
// see https://code.visualstudio.com/api/references/theme-color for more settings
@ -294,8 +296,8 @@ export const EditorUI = (props: EditorUIProps) => {
'menu.background' : textbackground ,
'menu.selectionBackground' : secondaryColor ,
'menu.selectionForeground' : textColor ,
'menu.selectionBorder' : secondaryColor
}
'menu.selectionBorder' : secondaryColor ,
} ,
} )
monacoRef . current . editor . setTheme ( themeName )
}
@ -313,7 +315,7 @@ export const EditorUI = (props: EditorUIProps) => {
const file = editorModelsState [ props . currentFile ]
editorRef . current . setModel ( file . model )
editorRef . current . updateOptions ( {
readOnly : editorModelsState [ props . currentFile ] . readOnly
readOnly : editorModelsState [ props . currentFile ] . readOnly ,
} )
if ( file . language === 'sol' ) {
monacoRef . current . editor . setModelLanguage ( file . model , 'remix-solidity' )
@ -337,10 +339,10 @@ export const EditorUI = (props: EditorUIProps) => {
options : {
isWholeLine : false ,
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' } `
}
}
}
if ( typeOfDecoration === 'markerPerFile' ) {
@ -363,8 +365,8 @@ export const EditorUI = (props: EditorUIProps) => {
) ,
options : {
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 } ` ,
} ,
}
}
if ( typeOfDecoration === 'lineTextPerFile' ) {
@ -380,11 +382,11 @@ export const EditorUI = (props: EditorUIProps) => {
options : {
after : {
content : ` ${ lineTextDecoration . content } ` ,
inlineClassName : ` ${ lineTextDecoration . className } `
inlineClassName : ` ${ lineTextDecoration . className } ` ,
} ,
afterContentClassName : ` ${ lineTextDecoration . afterContentClassName } ` ,
hoverMessage : lineTextDecoration.hoverMessage
}
hoverMessage : lineTextDecoration.hoverMessage ,
} ,
}
}
if ( typeOfDecoration === 'lineTextPerFile' ) {
@ -400,11 +402,11 @@ export const EditorUI = (props: EditorUIProps) => {
options : {
after : {
content : ` ${ lineTextDecoration . content } ` ,
inlineClassName : ` ${ lineTextDecoration . className } `
inlineClassName : ` ${ lineTextDecoration . className } ` ,
} ,
afterContentClassName : ` ${ lineTextDecoration . afterContentClassName } ` ,
hoverMessage : lineTextDecoration.hoverMessage
}
hoverMessage : lineTextDecoration.hoverMessage ,
} ,
}
}
}
@ -414,7 +416,7 @@ export const EditorUI = (props: EditorUIProps) => {
if ( ! model )
return {
currentDecorations : [ ] ,
registeredDecorations : [ ]
registeredDecorations : [ ] ,
}
const decorations = [ ]
const newRegisteredDecorations = [ ]
@ -428,7 +430,7 @@ export const EditorUI = (props: EditorUIProps) => {
}
return {
currentDecorations : model.deltaDecorations ( currentDecorations , decorations ) ,
registeredDecorations : newRegisteredDecorations
registeredDecorations : newRegisteredDecorations ,
}
}
@ -436,7 +438,7 @@ export const EditorUI = (props: EditorUIProps) => {
const model = editorModelsState [ filePath ] ? . model
if ( ! model )
return {
currentDecorations : [ ]
currentDecorations : [ ] ,
}
const decorations = [ ]
if ( registeredDecorations ) {
@ -447,7 +449,7 @@ export const EditorUI = (props: EditorUIProps) => {
}
}
return {
currentDecorations : model.deltaDecorations ( currentDecorations , decorations )
currentDecorations : model.deltaDecorations ( currentDecorations , decorations ) ,
}
}
@ -457,7 +459,7 @@ export const EditorUI = (props: EditorUIProps) => {
const monacoDecoration = convertToMonacoDecoration ( decoration , typeOfDecoration )
return {
currentDecorations : model.deltaDecorations ( [ ] , [ monacoDecoration ] ) ,
registeredDecorations : [ { value : decoration , type : typeOfDecoration } ]
registeredDecorations : [ { value : decoration , type : typeOfDecoration } ] ,
}
}
@ -478,7 +480,7 @@ export const EditorUI = (props: EditorUIProps) => {
const errorServerityMap = {
error : MarkerSeverity.Error ,
warning : MarkerSeverity.Warning ,
info : MarkerSeverity.Info
info : MarkerSeverity.Info ,
}
if ( model ) {
const markerData : monacoTypes.editor.IMarkerData = {
@ -487,7 +489,7 @@ export const EditorUI = (props: EditorUIProps) => {
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
message : error.message ,
}
if ( ! allMarkersPerfile [ filePath ] ) {
allMarkersPerfile [ filePath ] = [ ]
@ -572,9 +574,9 @@ export const EditorUI = (props: EditorUIProps) => {
range : new monacoRef . current . Range ( position . lineNumber , 1 , position . lineNumber , 1 ) ,
options : {
isWholeLine : false ,
glyphMarginClassName : 'fas fa-circle text-info'
}
}
glyphMarginClassName : 'fas fa-circle text-info' ,
} ,
} ,
]
)
prevState [ currentFile ] [ position . lineNumber ] = decorationIds [ 0 ]
@ -626,7 +628,7 @@ export const EditorUI = (props: EditorUIProps) => {
< / div >
< / div >
< / div >
)
) ,
}
props . plugin . call ( 'notification' , 'alert' , modalContent )
pasteCodeRef . current = true
@ -649,7 +651,7 @@ export const EditorUI = (props: EditorUIProps) => {
contextMenuGroupId : 'zooming' , // create a new grouping
keybindings : [
// eslint-disable-next-line no-bitwise
monacoRef . current . KeyMod . CtrlCmd | monacoRef . current . KeyCode . Equal
monacoRef . current . KeyMod . CtrlCmd | monacoRef . current . KeyCode . Equal ,
] ,
run : ( ) = > {
editor . updateOptions ( { fontSize : editor.getOption ( 51 ) + 1 } )
@ -662,7 +664,7 @@ export const EditorUI = (props: EditorUIProps) => {
contextMenuGroupId : 'zooming' , // create a new grouping
keybindings : [
// eslint-disable-next-line no-bitwise
monacoRef . current . KeyMod . CtrlCmd | monacoRef . current . KeyCode . Minus
monacoRef . current . KeyMod . CtrlCmd | monacoRef . current . KeyCode . Minus ,
] ,
run : ( ) = > {
editor . updateOptions ( { fontSize : editor.getOption ( 51 ) - 1 } )
@ -675,12 +677,48 @@ export const EditorUI = (props: EditorUIProps) => {
contextMenuGroupId : 'formatting' , // create a new grouping
keybindings : [
// 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 ( ) = > {
const file = await props . plugin . call ( 'fileManager' , 'getCurrentFile' )
await props . plugin . call ( 'codeFormatter' , 'format' , file )
} ,
}
let gptGenerateDocumentationAction
const executeGptGenerateDocumentationAction = {
id : 'generateDocumentation' ,
label : 'Generate documentation for this function' ,
contextMenuOrder : 0 , // choose the order
contextMenuGroupId : 'gtp' , // create a new grouping
keybindings : [ ] ,
run : async ( ) = > {
const file = await props . plugin . call ( 'fileManager' , 'getCurrentFile' )
const content = await props . plugin . call ( 'fileManager' , 'readFile' , file )
const message = `
solidity code : $ { content }
Generate the documentation for the function $ { currentFunction . current } using the Doxygen style syntax
`
await props . plugin . call ( 'openaigpt' , 'message' , message )
} ,
}
let gptExplainFunctionAction
const executegptExplainFunctionAction = {
id : 'explainFunction' ,
label : 'Explain this function' ,
contextMenuOrder : 1 , // choose the order
contextMenuGroupId : 'gtp' , // create a new grouping
keybindings : [ ] ,
run : async ( ) = > {
const file = await props . plugin . call ( 'fileManager' , 'getCurrentFile' )
const content = await props . plugin . call ( 'fileManager' , 'readFile' , file )
const message = `
solidity code : $ { content }
Explain the function $ { currentFunction . current }
`
await props . plugin . call ( 'openaigpt' , 'message' , message )
} ,
}
const freeFunctionCondition = editor . createContextKey ( 'freeFunctionCondition' , false )
@ -693,7 +731,7 @@ export const EditorUI = (props: EditorUIProps) => {
precondition : 'freeFunctionCondition' ,
keybindings : [
// 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 ( ) = > {
const { nodesAtPosition } = await retrieveNodesAtPosition ( props . editorAPI , props . plugin )
@ -709,12 +747,14 @@ export const EditorUI = (props: EditorUIProps) => {
} 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.' )
}
}
} ,
}
editor . addAction ( formatAction )
editor . addAction ( zoomOutAction )
editor . addAction ( zoominAction )
freeFunctionAction = editor . addAction ( executeFreeFunctionAction )
gptGenerateDocumentationAction = editor . addAction ( executeGptGenerateDocumentationAction )
gptExplainFunctionAction = editor . addAction ( executegptExplainFunctionAction )
// 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 ( ) )
@ -726,6 +766,15 @@ export const EditorUI = (props: EditorUIProps) => {
freeFunctionAction . dispose ( )
freeFunctionAction = null
}
if ( gptGenerateDocumentationAction ) {
gptGenerateDocumentationAction . dispose ( )
gptGenerateDocumentationAction = null
}
if ( gptExplainFunctionAction ) {
gptExplainFunctionAction . dispose ( )
gptExplainFunctionAction = null
}
const file = await props . plugin . call ( 'fileManager' , 'getCurrentFile' )
if ( ! file . endsWith ( '.sol' ) ) {
freeFunctionCondition . set ( false )
@ -737,6 +786,14 @@ export const EditorUI = (props: EditorUIProps) => {
executeFreeFunctionAction . label = ` Run the free function " ${ freeFunctionNode . name } " in the Remix VM `
freeFunctionAction = editor . addAction ( executeFreeFunctionAction )
}
const functionImpl = nodesAtPosition . find ( ( node ) = > node . kind === 'function' )
if ( functionImpl ) {
currentFunction . current = functionImpl . name
executeGptGenerateDocumentationAction . label = ` Generate documentation for the function " ${ functionImpl . name } " `
gptGenerateDocumentationAction = editor . addAction ( executeGptGenerateDocumentationAction )
executegptExplainFunctionAction . label = ` Explain the function " ${ functionImpl . name } " `
gptExplainFunctionAction = editor . addAction ( executegptExplainFunctionAction )
}
freeFunctionCondition . set ( ! ! freeFunctionNode )
}
contextmenu . _onContextMenu = ( . . . args ) = > {
@ -757,7 +814,7 @@ export const EditorUI = (props: EditorUIProps) => {
editor . revealRange ( input . options . selection )
editor . setPosition ( {
column : input.options.selection.startColumn ,
lineNumber : input.options.selection.startLineNumber
lineNumber : input.options.selection.startLineNumber ,
} )
}
} catch ( e ) {
@ -817,7 +874,7 @@ export const EditorUI = (props: EditorUIProps) => {
beforeMount = { handleEditorWillMount }
options = { {
glyphMargin : true ,
readOnly : ( ! editorRef . current || ! props . currentFile ) && editorModelsState [ props . currentFile ] ? . readOnly
readOnly : ( ! editorRef . current || ! props . currentFile ) && editorModelsState [ props . currentFile ] ? . readOnly ,
} }
defaultValue = { defaultEditorValue }
/ >