add gast costs in editor

editorcontextDummy
filip mertens 2 years ago
parent 9222f96891
commit 2356bd8e0d
  1. 18
      apps/remix-ide/src/app/editor/editor.js
  2. 193
      apps/remix-ide/src/app/plugins/code-parser.tsx
  3. 75
      apps/remix-ide/src/lib/offsetToLineColumnConverter.js
  4. 10
      libs/remix-core-plugin/src/lib/offset-line-to-column-converter.ts
  5. 1
      libs/remix-debug/src/source/sourceMappingDecoder.ts
  6. 46
      libs/remix-ui/editor/src/lib/providers/codeLensProvider.ts
  7. 13
      libs/remix-ui/editor/src/lib/providers/hoverProvider.ts
  8. 70
      libs/remix-ui/editor/src/lib/remix-ui-editor.tsx
  9. 11
      libs/remix-ui/file-decorators/src/lib/components/file-decoration-icon.tsx
  10. 6
      libs/remix-ui/file-decorators/src/lib/components/filedecorationicons/file-decoration-custom-icon.tsx
  11. 26
      libs/remix-ui/file-decorators/src/lib/components/filedecorationicons/file-decoration-error-icon.tsx
  12. 33
      libs/remix-ui/file-decorators/src/lib/components/filedecorationicons/file-decoration-tooltip.tsx
  13. 4
      libs/remix-ui/file-decorators/src/lib/components/filedecorationicons/file-decoration-warning-icon.tsx
  14. 11
      libs/remix-ui/file-decorators/src/lib/helper/index.tsx
  15. 1
      libs/remix-ui/workspace/src/lib/components/file-label.tsx

@ -13,7 +13,7 @@ const profile = {
name: 'editor',
description: 'service - editor',
version: packageJson.version,
methods: ['highlight', 'discardHighlight', 'clearAnnotations', 'addAnnotation', 'gotoLine', 'revealRange', 'getCursorPosition', 'open', 'addModel', 'addErrorMarker', 'clearErrorMarkers']
methods: ['highlight', 'discardHighlight', 'clearAnnotations', 'addLineText', 'discardLineTexts', 'addAnnotation', 'gotoLine', 'revealRange', 'getCursorPosition', 'open', 'addModel', 'addErrorMarker', 'clearErrorMarkers']
}
class Editor extends Plugin {
@ -26,8 +26,8 @@ class Editor extends Plugin {
remixDark: 'remix-dark'
}
this.registeredDecorations = { sourceAnnotationsPerFile: {}, markerPerFile: {} }
this.currentDecorations = { sourceAnnotationsPerFile: {}, markerPerFile: {} }
this.registeredDecorations = { sourceAnnotationsPerFile: {}, markerPerFile: {}, lineTextPerFile: {} }
this.currentDecorations = { sourceAnnotationsPerFile: {}, markerPerFile: {}, lineTextPerFile: {} }
// Init
this.event = new EventManager()
@ -580,6 +580,18 @@ class Editor extends Plugin {
this.clearDecorationsByPlugin(session, from, 'markerPerFile', this.registeredDecorations, this.currentDecorations)
}
}
async addLineText (lineText, filePath) {
filePath = filePath || this.currentFile
await this.addDecoration(lineText, filePath, 'lineTextPerFile')
}
discardLineTexts() {
const { from } = this.currentRequest
for (const session in this.sessions) {
this.clearDecorationsByPlugin(session, from, 'lineTextPerFile', this.registeredDecorations, this.currentDecorations)
}
}
}
module.exports = Editor

@ -9,6 +9,7 @@ import { helper } from '@remix-project/remix-solidity'
import React from 'react'
import { fileDecoration, fileDecorationType } from '@remix-ui/file-decorators'
import { lineText } from '@remix-ui/editor'
// eslint-disable-next-line
@ -16,7 +17,7 @@ const SolidityParser = (window as any).SolidityParser = (window as any).Solidity
const profile = {
name: 'codeParser',
methods: ['nodesAtPosition', 'getFunctionParamaters', 'getDeclaration', 'getFunctionReturnParameters', 'getVariableDeclaration', 'getNodeDocumentation', 'getNodeLink', 'listAstNodes', 'getBlockAtPosition', 'getLastNodeInLine', 'resolveImports', 'parseSolidity', 'getNodesWithScope', 'getNodesWithName', 'getNodes', 'compile', 'getNodeById', 'getLastCompilationResult', 'positionOfDefinition', 'definitionAtPosition', 'jumpToDefinition', 'referrencesAtPosition', 'referencesOf', 'getActiveHighlights', 'gasEstimation', 'declarationOf'],
methods: ['nodesAtPosition', 'getFunctionParamaters', 'getDeclaration', 'getFunctionReturnParameters', 'getVariableDeclaration', 'getNodeDocumentation', 'getNodeLink', 'listAstNodes', 'getBlockAtPosition', 'getLastNodeInLine', 'resolveImports', 'parseSolidity', 'getNodesWithScope', 'getNodesWithName', 'getNodes', 'compile', 'getNodeById', 'getLastCompilationResult', 'positionOfDefinition', 'definitionAtPosition', 'jumpToDefinition', 'referrencesAtPosition', 'referencesOf', 'getActiveHighlights', 'gasEstimation', 'declarationOf', 'getGasEstimates'],
events: [],
version: '0.0.1'
}
@ -40,6 +41,7 @@ export class CodeParser extends Plugin {
astWalker: any
errorState: boolean = false
onAstFinished: (success: any, data: CompilationResult, source: CompilationSource, input: any, version: any) => Promise<void>
gastEstimateTimeOut: any
constructor(astWalker) {
super(profile)
@ -53,16 +55,18 @@ export class CodeParser extends Plugin {
async onActivation() {
this.on('editor', 'didChangeFile', async (file) => {
console.log('contentChanged', file)
await this.call('editor', 'discardLineTexts')
await this.getCurrentFileAST()
await this.compile()
})
this.on('filePanel', 'setWorkspace', async() => {
this.on('filePanel', 'setWorkspace', async () => {
await this.call('fileDecorator', 'setFileDecorators', [])
})
this.on('fileManager', 'currentFileChanged', async () => {
await this.call('editor', 'discardLineTexts')
await this.getCurrentFileAST()
await this.compile()
})
@ -101,7 +105,6 @@ export class CodeParser extends Plugin {
for (const error of data.errors) {
const pos = helper.getPositionDetails(error.formattedMessage)
const filePosition = Object.keys(sources).findIndex((fileName) => fileName === error.sourceLocation.file)
const source = sources[pos.file]
const lineColumn = await this.call('offsetToLineColumnConverter', 'offsetToLineColumn',
{
start: error.sourceLocation.start,
@ -109,7 +112,7 @@ export class CodeParser extends Plugin {
},
filePosition,
result.getSourceCode().sources,
result.getAsts())
null)
allErrors.push({ error, lineColumn })
}
console.log('allErrors', allErrors)
@ -196,19 +199,33 @@ export class CodeParser extends Plugin {
decorators.push(decorator)
}
console.log(decorators)
await this.call('fileDecorator', 'setFileDecorators', decorators)
}
if (!data.sources) return
if (data.sources && Object.keys(data.sources).length === 0) return
this.lastCompilationResult = new CompilerAbstract('soljson', data, source, input)
this.errorState = false
this._index = {
Declarations: {},
FlatReferences: {}
FlatReferences: {},
NodesPerFile: {},
}
this._buildIndex(data, source)
if (this.gastEstimateTimeOut) {
window.clearTimeout(this.gastEstimateTimeOut)
}
this.gastEstimateTimeOut = window.setTimeout(async () => {
this.setGasEstimates()
}, 500)
console.log("INDEX", this._index)
this.emit('astFinished')
}
@ -274,13 +291,10 @@ export class CodeParser extends Plugin {
const fileContent = text || await this.call('fileManager', 'readFile', this.currentFile)
try {
const ast = await this.parseSolidity(fileContent)
this.currentFileAST = ast
console.log('AST PARSE SUCCESS', ast)
} catch (e) {
console.log(e)
}
console.log('LAST PARSER AST', this.currentFileAST)
return this.currentFileAST
}
@ -303,12 +317,86 @@ export class CodeParser extends Plugin {
for (const s in compilationResult.sources) {
this.astWalker.walkFull(compilationResult.sources[s].ast, callback)
}
console.log("INDEX", this._index)
}
}
// NODE HELPERS
_getInputParams(node) {
const params = []
const target = node.parameters
if (target) {
const children = target.parameters
for (const j in children) {
if (children[j].nodeType === 'VariableDeclaration') {
params.push(children[j].typeDescriptions.typeString)
}
}
}
return '(' + params.toString() + ')'
}
_flatNodeList(node: any, contractName: string, fileName: string, compilatioResult: any) {
const index = {}
const callback = (node) => {
node.gasEstimate = this._getContractGasEstimate(node, contractName, fileName, compilatioResult)
node.functionName = node.name + this._getInputParams(node)
index[node.id] = node
}
this.astWalker.walkFull(node, callback)
return index
}
_extractFileNodes(fileName: string, compilatioResult: any) {
const source = compilatioResult.data.sources[fileName]
const nodesByContract = []
this.astWalker.walkFull(source.ast, (node) => {
if (node.nodeType === 'ContractDefinition') {
const flatNodes = this._flatNodeList(node, node.name, fileName, compilatioResult)
node.gasEstimate = this._getContractGasEstimate(node, node.name, fileName, compilatioResult)
nodesByContract[node.name] = { contractDefinition: node, contractNodes: flatNodes }
}
})
return nodesByContract
}
_getContractGasEstimate(node: any, contractName: string, fileName: string, compilationResult: any) {
const contracts = compilationResult.data.contracts && compilationResult.data.contracts[this.currentFile]
for (const name in contracts) {
if (name === contractName) {
const contract = contracts[name]
const estimationObj = contract.evm && contract.evm.gasEstimates
if (node.nodeType === 'ContractDefinition') {
return {
creationCost: estimationObj === null ? '-' : estimationObj.creation.totalCost,
codeDepositCost: estimationObj === null ? '-' : estimationObj.creation.codeDepositCost,
}
}
let executionCost = null
if (node.nodeType === 'FunctionDefinition') {
const visibility = node.visibility
if (node.kind !== 'constructor') {
const fnName = node.name
const fn = fnName + this._getInputParams(node)
if (visibility === 'public' || visibility === 'external') {
executionCost = estimationObj === null ? '-' : estimationObj.external[fn]
} else if (visibility === 'private' || visibility === 'internal') {
executionCost = estimationObj === null ? '-' : estimationObj.internal[fn]
}
return { executionCost }
}
}
}
}
}
/**
* Returns the block surrounding the given position
* For example if the position is in the middle of a function, it will return the function
@ -317,7 +405,6 @@ export class CodeParser extends Plugin {
* @return {any}
* */
async getBlockAtPosition(position: any, text: string = null) {
console.log('GET BLOCK AT ', position)
await this.getCurrentFileAST(text)
const allowedTypes = ['SourceUnit', 'ContractDefinition', 'FunctionDefinition']
@ -397,6 +484,7 @@ export class CodeParser extends Plugin {
const lastCompilationResult = this.lastCompilationResult
if (!lastCompilationResult) return false
const urlFromPath = await this.call('fileManager', 'getUrlFromPath', this.currentFile)
console.log('URL FROM PATH', urlFromPath)
if (lastCompilationResult && lastCompilationResult.languageversion.indexOf('soljson') === 0 && lastCompilationResult.data && lastCompilationResult.data.sources && lastCompilationResult.data.sources[this.currentFile]) {
const nodes = sourceMappingDecoder.nodesAtPosition(type, position, lastCompilationResult.data.sources[this.currentFile] || lastCompilationResult.data.sources[urlFromPath.file])
return nodes
@ -656,15 +744,23 @@ export class CodeParser extends Plugin {
* @returns
*/
async getNodeLink(node: any) {
const lineColumn = await this.getNodeLineColumn(node)
return lineColumn ? `${lineColumn.fileName} ${lineColumn.position.start.line}:${lineColumn.position.start.column}` : null
}
/*
* @param node
*/
async getNodeLineColumn(node: any) {
const position = await this.positionOfDefinition(node)
if (position) {
const filename = this.lastCompilationResult.getSourceName(position.file)
const lineColumn = await this.call('offsetToLineColumnConverter', 'offsetToLineColumn',
position,
position.file,
this.lastCompilationResult.getSourceCode().sources,
this.lastCompilationResult.getAsts())
return `${filename} ${lineColumn.start.line}:${lineColumn.start.column}`
const fileName = this.lastCompilationResult.getSourceName(position.file)
const lineBreaks = sourceMappingDecoder.getLinebreakPositions(this.lastCompilationResult.source.sources[fileName].content)
const lineColumn = sourceMappingDecoder.convertOffsetToLineColumn(position, lineBreaks)
return {
fileName,
position: lineColumn
}
}
}
@ -736,6 +832,69 @@ export class CodeParser extends Plugin {
}
}
/**
*
* @param fileName
*/
async getGasEstimates(fileName: string) {
if (!fileName) {
fileName = await this.currentFile
}
if (this._index.NodesPerFile && this._index.NodesPerFile[fileName]) {
const estimates = []
for (const contract in this._index.NodesPerFile[fileName]) {
console.log(contract)
const nodes = this._index.NodesPerFile[fileName][contract].contractNodes
for (const node of Object.values(nodes) as any[]) {
if (node.gasEstimate) {
estimates.push({
node,
range: await this.getNodeLineColumn(node)
})
}
}
}
return estimates
}
}
async setGasEstimates() {
this.currentFile = await this.call('fileManager', 'file')
this._index.NodesPerFile[this.currentFile] = await this._extractFileNodes(this.currentFile, this.lastCompilationResult)
const gasEstimates = await this.getGasEstimates(this.currentFile)
console.log('all estimates', gasEstimates)
const friendlyNames = {
'executionCost': 'Estimated execution cost',
'codeDepositCost': 'Estimated code deposit cost',
'creationCost': 'Estimated creation cost',
}
await this.call('editor', 'discardLineTexts')
if (gasEstimates) {
for (const estimate of gasEstimates) {
console.log(estimate)
const linetext: lineText = {
content: Object.entries(estimate.node.gasEstimate).map(([key, value]) => `${value} gas`).join(' '),
position: estimate.range.position,
hide: false,
className: 'text-muted small',
afterContentClassName: 'text-muted small fas fa-gas-pump pl-4',
from: 'codeParser',
hoverMessage: [{
value: `${Object.entries(estimate.node.gasEstimate).map(([key, value]) => `${friendlyNames[key]}: ${value} gas`).join(' ')}`,
},
],
}
this.call('editor', 'addLineText', linetext, estimate.range.fileName)
}
}
}
}

@ -1,75 +0,0 @@
'use strict'
import { Plugin } from '@remixproject/engine'
import * as packageJson from '../../../../package.json'
import { sourceMappingDecoder } from '@remix-project/remix-debug'
const profile = {
name: 'offsetToLineColumnConverter',
methods: ['offsetToLineColumn'],
events: [],
version: packageJson.version
}
export class OffsetToLineColumnConverter extends Plugin {
constructor () {
super(profile)
this.lineBreakPositionsByContent = {}
this.sourceMappingDecoder = sourceMappingDecoder
}
/**
* Convert offset representation with line/column representation.
* This is also used to resolve the content:
* @arg file is the index of the file in the content sources array and content sources array does have filename as key and not index.
* So we use the asts (which references both index and filename) to look up the actual content targeted by the @arg file index.
* @param {{start, length}} rawLocation - offset location
* @param {number} file - The index where to find the source in the sources parameters
* @param {Object.<string, {content}>} sources - Map of content sources
* @param {Object.<string, {ast, id}>} asts - Map of content sources
*/
offsetToLineColumn (rawLocation, file, sources, asts) {
if (!this.lineBreakPositionsByContent[file]) {
const sourcesArray = Object.keys(sources)
if (!asts || (file === 0 && sourcesArray.length === 1)) {
// if we don't have ast, we process the only one available content (applicable also for compiler older than 0.4.12)
this.lineBreakPositionsByContent[file] = this.sourceMappingDecoder.getLinebreakPositions(sources[sourcesArray[0]].content)
} else {
for (var filename in asts) {
const source = asts[filename]
if (source.id === file) {
this.lineBreakPositionsByContent[file] = this.sourceMappingDecoder.getLinebreakPositions(sources[filename].content)
break
}
}
}
}
return this.sourceMappingDecoder.convertOffsetToLineColumn(rawLocation, this.lineBreakPositionsByContent[file])
}
/**
* Convert offset representation with line/column representation.
* @param {{start, length}} rawLocation - offset location
* @param {number} file - The index where to find the source in the sources parameters
* @param {string} content - source
*/
offsetToLineColumnWithContent (rawLocation, file, content) {
this.lineBreakPositionsByContent[file] = this.sourceMappingDecoder.getLinebreakPositions(content)
return this.sourceMappingDecoder.convertOffsetToLineColumn(rawLocation, this.lineBreakPositionsByContent[file])
}
/**
* Clear the cache
*/
clear () {
this.lineBreakPositionsByContent = {}
}
/**
* called by plugin API
*/
activate () {
this.on('solidity', 'compilationFinished', () => {
this.clear()
})
}
}

@ -30,13 +30,15 @@ export class OffsetToLineColumnConverter extends Plugin {
* @param {Object.<string, {ast, id}>} asts - Map of content sources
*/
offsetToLineColumn (rawLocation, file, sources, asts) {
console.log('offsetToLineColumn', sources)
//if (!this.lineBreakPositionsByContent[file]) {
const sourcesArray = Object.keys(sources)
if (!asts || (file === 0 && sourcesArray.length === 1)) {
if (!asts || (file === 0 && sourcesArray.length === 1) || !Array.isArray(asts)) {
console.log("no asts or only one file", sourcesArray, sources, sources[sourcesArray[file || 0]].content)
// if we don't have ast, we process the only one available content (applicable also for compiler older than 0.4.12)
this.lineBreakPositionsByContent[file] = this.sourceMappingDecoder.getLinebreakPositions(sources[sourcesArray[0]].content)
this.lineBreakPositionsByContent[file || 0] = this.sourceMappingDecoder.getLinebreakPositions(sources[sourcesArray[file || 0]].content)
} else {
console.log("have asts")
for (const filename in asts) {
const source = asts[filename]
if (source.id === file) {
@ -46,7 +48,7 @@ export class OffsetToLineColumnConverter extends Plugin {
}
}
//}
return this.sourceMappingDecoder.convertOffsetToLineColumn(rawLocation, this.lineBreakPositionsByContent[file])
return this.sourceMappingDecoder.convertOffsetToLineColumn(rawLocation, this.lineBreakPositionsByContent[file || 0])
}
/**

@ -68,6 +68,7 @@ export function getLinebreakPositions (source) {
* @return {Object} returns an object {start: {line, column}, end: {line, column}} (line/column count start at 0)
*/
export function convertOffsetToLineColumn (sourceLocation, lineBreakPositions) {
console.log(sourceLocation, lineBreakPositions)
if (sourceLocation.start >= 0 && sourceLocation.length >= 0) {
return {
start: convertFromCharPosition(sourceLocation.start, lineBreakPositions),

@ -0,0 +1,46 @@
import { lineText } from "../remix-ui-editor"
export class RemixCodeLensProvider {
props: any
monaco: any
constructor(props: any, monaco: any) {
this.props = props
this.monaco = monaco
}
async provideCodeLenses(model: any, token: any) {
const gasEstimates = await this.props.plugin.call('codeParser', 'getGasEstimates')
const decorations = []
const friendlyNames = {
'executionCost': 'Execution Cost',
'codeDepositCost': 'Code Deposit Cost',
'creationCost': 'Creation Cost',
}
/* if (gasEstimates) {
for (const estimate of gasEstimates) {
console.log(estimate)
const linetext: lineText = {
content: Object.entries(estimate.node.gasEstimate).map(([key, value]) => `${friendlyNames[key]}: ${value} gas`).join(' '),
position: estimate.range.position,
hide: false,
className: 'remix-code-lens',
from: 'remix-code-lens',
}
this.props.plugin.call('editor', 'addLineText', linetext, estimate.range.fileName)
}
} */
return {
lenses: [],
dispose: () => { }
};
}
}

@ -1,6 +1,5 @@
import { editor, languages, Position } from 'monaco-editor'
import monaco from '../../types/monaco'
import { EditorUIProps } from '../remix-ui-editor'
export class RemixHoverProvider implements languages.HoverProvider {
@ -62,7 +61,7 @@ export class RemixHoverProvider implements languages.HoverProvider {
const getOverrides = async (node: any) => {
if (node.overrides) {
let overrides = []
const overrides = []
for (const override of node.overrides.overrides) {
overrides.push(override.name)
}
@ -73,7 +72,7 @@ export class RemixHoverProvider implements languages.HoverProvider {
}
const getlinearizedBaseContracts = async (node: any) => {
let params = []
const params = []
if (node.linearizedBaseContracts) {
for (const id of node.linearizedBaseContracts) {
const baseContract = await this.props.plugin.call('codeParser', 'getNodeById', id)
@ -111,10 +110,10 @@ export class RemixHoverProvider implements languages.HoverProvider {
})
} else if (nodeAtPosition.nodeType === 'FunctionDefinition') {
if(!nodeAtPosition.name) return
if (!nodeAtPosition.name) return
const returns = await getReturnParameters(nodeAtPosition)
contents.push({
value: `function ${nodeAtPosition.name} ${await getParamaters(nodeAtPosition)} ${nodeAtPosition.visibility} ${nodeAtPosition.stateMutability}${await getOverrides(nodeAtPosition)} ${returns? `returns ${returns}`: ''}`
value: `function ${nodeAtPosition.name} ${await getParamaters(nodeAtPosition)} ${nodeAtPosition.visibility} ${nodeAtPosition.stateMutability}${await getOverrides(nodeAtPosition)} ${returns ? `returns ${returns}` : ''}`
})
} else if (nodeAtPosition.nodeType === 'ModifierDefinition') {
@ -134,6 +133,8 @@ export class RemixHoverProvider implements languages.HoverProvider {
contents.push({
value: `There are errors in the code.`
})
} else if (nodeAtPosition.nodeType === 'Block') {
} else {
contents.push({
value: `${nodeAtPosition.nodeType}`
@ -146,7 +147,7 @@ export class RemixHoverProvider implements languages.HoverProvider {
}
getLinks(nodeAtPosition)
getDocs(nodeAtPosition)
getScope(nodeAtPosition)
// getScope(nodeAtPosition)
}

@ -8,20 +8,12 @@ import { cairoLang, cairoConf } from './cairoSyntax'
import './remix-ui-editor.css'
import { loadTypes } from './web-types'
import monaco from '../types/monaco'
import { IPosition, MarkerSeverity } from 'monaco-editor'
import { IMarkdownString, IPosition, MarkerSeverity } from 'monaco-editor'
import { RemixHoverProvider } from './providers/hoverProvider'
import { RemixReferenceProvider } from './providers/referenceProvider'
import { RemixCompletionProvider } from './providers/completionProvider'
import { RemixSignatureProvider } from './providers/signatureProvider'
import { CompilationError } from '@remix-project/remix-solidity-ts'
type cursorPosition = {
startLineNumber: number,
startColumn: number,
endLineNumber: number,
endColumn: number
}
import { RemixCodeLensProvider } from './providers/codeLensProvider'
type sourceAnnotation = {
row: number,
@ -47,6 +39,25 @@ type sourceMarker = {
hide: boolean
}
export type lineText = {
position: {
start: {
line: number
column: number
},
end: {
line: number
column: number
}
},
from: string // plugin name
content: string
className: string
afterContentClassName: string
hide: boolean,
hoverMessage: IMarkdownString | IMarkdownString[]
}
loader.config({ paths: { vs: 'assets/js/monaco-editor/dev/vs' } })
export type DecorationsReturn = {
@ -270,7 +281,7 @@ export const EditorUI = (props: EditorUIProps) => {
}, [props.currentFile])
const convertToMonacoDecoration = (decoration: sourceAnnotation | sourceMarker, typeOfDecoration: string) => {
const convertToMonacoDecoration = (decoration: any, typeOfDecoration: string) => {
if (typeOfDecoration === 'sourceAnnotationsPerFile') {
decoration = decoration as sourceAnnotation
return {
@ -300,6 +311,20 @@ export const EditorUI = (props: EditorUIProps) => {
}
}
}
if (typeOfDecoration === 'lineTextPerFile') {
console.log('lineTextPerFile', decoration)
decoration = decoration as lineText
return {
type: typeOfDecoration,
range: new monacoRef.current.Range(decoration.position.start.line + 1, decoration.position.start.column + 1, decoration.position.start.line + 1, 1024),
options: {
after: { content: ` ${decoration.content}`, inlineClassName: `${decoration.className}` },
afterContentClassName: `${decoration.afterContentClassName}`,
hoverMessage : decoration.hoverMessage
},
}
}
}
props.editorAPI.clearDecorationsByPlugin = (filePath: string, plugin: string, typeOfDecoration: string, registeredDecorations: any, currentDecorations: any) => {
@ -318,6 +343,7 @@ export const EditorUI = (props: EditorUIProps) => {
}
}
}
console.log(decorations, currentDecorations)
return {
currentDecorations: model.deltaDecorations(currentDecorations, decorations),
registeredDecorations: newRegisteredDecorations
@ -343,10 +369,11 @@ export const EditorUI = (props: EditorUIProps) => {
}
const addDecoration = (decoration: sourceAnnotation | sourceMarker, filePath: string, typeOfDecoration: string) => {
console.log("addDecoration", decoration, filePath, typeOfDecoration)
const model = editorModelsState[filePath]?.model
if (!model) return { currentDecorations: [] }
const monacoDecoration = convertToMonacoDecoration(decoration, typeOfDecoration)
console.log(monacoDecoration)
return {
currentDecorations: model.deltaDecorations([], [monacoDecoration]),
registeredDecorations: [{ value: decoration, type: typeOfDecoration }]
@ -378,10 +405,10 @@ 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,
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)
@ -495,6 +522,7 @@ export const EditorUI = (props: EditorUIProps) => {
}
function handleEditorWillMount(monaco: Monaco) {
// MonacoEditorTextDecorationPatch.augmentEditor(monaco.editor)
console.log('editor will mount', monaco, typeof monaco)
monacoRef.current = monaco
// Register a new language
@ -526,7 +554,7 @@ export const EditorUI = (props: EditorUIProps) => {
console.log('HIghlight', position)
const hightlights = [
{
range: new monacoRef.current.Range(position.lineNumber, position.column, position.lineNumber, position.column+5),
range: new monacoRef.current.Range(position.lineNumber, position.column, position.lineNumber, position.column + 5),
kind: monacoRef.current.languages.DocumentHighlightKind.Write
}
]
@ -534,12 +562,20 @@ export const EditorUI = (props: EditorUIProps) => {
}
})
// monacoRef.current.languages.registerCodeLensProvider('remix-solidity', new RemixCodeLensProvider(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))
// monacoRef.current.languages.registerSignatureHelpProvider('remix-solidity', new RemixSignatureProvider(props, monaco))
loadTypes(monacoRef.current)
monacoRef.current.languages.registerDefinitionProvider('typescript', {
provideDefinition(model: any, position: any, token: any) {
console.log(token)
return null
}
})
}
return (

@ -4,6 +4,7 @@ import React, { useEffect, useState } from 'react'
import { fileDecoration, fileDecorationType, FileType } from '../types'
import FileDecorationCustomIcon from './filedecorationicons/file-decoration-custom-icon'
import FileDecorationErrorIcon from './filedecorationicons/file-decoration-error-icon'
import FileDecorationTooltip from './filedecorationicons/file-decoration-tooltip'
import FileDecorationWarningIcon from './filedecorationicons/file-decoration-warning-icon'
export type fileDecorationProps = {
@ -19,23 +20,25 @@ export const FileDecorationIcons = (props: fileDecorationProps) => {
setStates(props.fileDecorations.filter((fileDecoration) => fileDecoration.path === props.file.path || `${fileDecoration.workspace.name}/${fileDecoration.path}` === props.file.path))
}, [props.fileDecorations])
const getTags = function () {
if (states && states.length) {
const elements: JSX.Element[] = []
for (const [index, state] of states.entries()) {
switch (state.fileStateType) {
case fileDecorationType.Error:
elements.push(<FileDecorationErrorIcon fileState={state} key={index} />)
elements.push(<FileDecorationTooltip key={index} index={index} fileDecoration={state} icon={<FileDecorationErrorIcon fileDecoration={state} key={index}/>}/>)
break
case fileDecorationType.Warning:
elements.push(<FileDecorationWarningIcon fileState={state} key={index}/>)
elements.push(<FileDecorationTooltip key={index} index={index} fileDecoration={state} icon={<FileDecorationWarningIcon fileDecoration={state} key={index}/>}/>)
break
case fileDecorationType.Custom:
elements.push(<FileDecorationCustomIcon fileState={state} key={index} />)
elements.push(<FileDecorationTooltip key={index} index={index} fileDecoration={state} icon={<FileDecorationCustomIcon fileDecoration={state} key={index}/>}/>)
break
}
}
return elements
}
}

@ -3,10 +3,10 @@ import React from 'react'
import { fileDecoration } from '../../types'
const FileDecorationCustomIcon = (props: {
fileState: fileDecoration
fileDecoration: fileDecoration
}) => {
return <><span className={`${props.fileState.fileStateIconClass}pr-2`}>
{props.fileState.fileStateIcon}
return <><span className={`${props.fileDecoration.fileStateIconClass}pr-2`}>
{props.fileDecoration.fileStateIcon}
</span></>
}

@ -1,33 +1,13 @@
// eslint-disable-next-line no-use-before-define
import React from 'react'
import { OverlayTrigger, Tooltip } from 'react-bootstrap'
import { fileDecoration } from '../../types'
const FileDecorationErrorIcon = (props: {
fileState: fileDecoration
fileDecoration: fileDecoration
}) => {
const getComments = function () {
if(props.fileState.commment){
const commments = Array.isArray(props.fileState.commment) ? props.fileState.commment : [props.fileState.commment]
return commments.map((comment, index) => {
return <div key={index}>{comment}<br></br></div>
})
}
}
return <>
<OverlayTrigger
placement='auto'
overlay={
<Tooltip id={`tooltip-${props.fileState.path}`}>
<>{getComments()}</>
</Tooltip>
}
>
<span className={`${props.fileState.fileStateIconClass} text-danger pr-2`}>{props.fileState.text}</span>
</OverlayTrigger>
<span className={`${props.fileDecoration.fileStateIconClass} text-danger pr-2`}>{props.fileDecoration.text}</span>
</>
}

@ -0,0 +1,33 @@
import React from "react";
import { OverlayTrigger, Tooltip } from "react-bootstrap";
import { fileDecoration } from "../../types";
const FileDecorationTooltip = (props: {
fileDecoration: fileDecoration,
icon: JSX.Element
index: number
},
) => {
const getComments = function (fileDecoration: fileDecoration) {
if (fileDecoration.commment) {
const commments = Array.isArray(fileDecoration.commment) ? fileDecoration.commment : [fileDecoration.commment]
return commments.map((comment, index) => {
return <div key={index}>{comment}<br></br></div>
})
}
}
return <OverlayTrigger
key={`overlaytrigger-${props.fileDecoration.path}-${props.index}`}
placement='auto'
overlay={
<Tooltip id={`error-tooltip-${props.fileDecoration.path}`}>
<>{getComments(props.fileDecoration)}</>
</Tooltip>
}
><div>{props.icon}</div></OverlayTrigger>
}
export default FileDecorationTooltip;

@ -3,9 +3,9 @@ import React from 'react'
import { fileDecoration } from '../../types'
const FileDecorationWarningIcon = (props: {
fileState: fileDecoration
fileDecoration: fileDecoration
}) => {
return <><span className={`${props.fileState.fileStateIconClass} text-warning pr-2`}>{props.fileState.text}</span></>
return <><span className={`${props.fileDecoration.fileStateIconClass} text-warning pr-2`}>{props.fileDecoration.text}</span></>
}
export default FileDecorationWarningIcon

@ -0,0 +1,11 @@
import React from "react"
import { fileDecoration } from "../types"
export const getComments = function (fileDecoration: fileDecoration) {
if(fileDecoration.commment){
const commments = Array.isArray(fileDecoration.commment) ? fileDecoration.commment : [fileDecoration.commment]
return commments.map((comment, index) => {
return <div key={index}>{comment}<br></br></div>
})
}
}

@ -28,7 +28,6 @@ export const FileLabel = (props: FileLabelProps) => {
}, [file.path, focusEdit])
useEffect(() => {
console.log('fileState', fileDecorations, file.name)
const state = props.fileDecorations.find((state: fileDecoration) => {
if(state.path === props.file.path) return true
if(state.bubble && props.file.isDirectory && state.path.startsWith(props.file.path)) return true

Loading…
Cancel
Save