optimize config for compiler

editorcontextDummy
filip mertens 2 years ago
parent 12d0c6a25e
commit 177771939f
  1. 21
      apps/remix-ide/src/app/plugins/parser/code-parser.tsx
  2. 31
      apps/remix-ide/src/app/plugins/parser/services/code-parser-antlr-service.ts
  3. 243
      apps/remix-ide/src/app/plugins/parser/services/code-parser-compiler.ts
  4. 3
      libs/remix-core-plugin/src/lib/offset-line-to-column-converter.ts
  5. 85
      libs/remix-ui/editor/src/lib/providers/completionProvider.ts
  6. 1
      libs/remix-ui/editor/src/lib/remix-ui-editor.tsx

@ -56,6 +56,7 @@ export class CodeParser extends Plugin {
getLastNodeInLine: (ast: string) => Promise<any>
listAstNodes: () => Promise<any>
getBlockAtPosition: (position: any, text?: string) => Promise<any>
getCurrentFileAST: (text?: string) => Promise<any>
constructor(astWalker) {
super(profile)
@ -73,10 +74,11 @@ export class CodeParser extends Plugin {
this.antlrService = new CodeParserAntlrService(this)
this.nodeHelper = new CodeParserNodeHelper(this)
this.parseSolidity = this.antlrService.parseSolidity
this.getLastNodeInLine = this.antlrService.getLastNodeInLine
this.listAstNodes = this.antlrService.listAstNodes
this.getBlockAtPosition = this.antlrService.getBlockAtPosition
this.parseSolidity = this.antlrService.parseSolidity.bind(this.antlrService)
this.getLastNodeInLine = this.antlrService.getLastNodeInLine.bind(this.antlrService)
this.listAstNodes = this.antlrService.listAstNodes.bind(this.antlrService)
this.getBlockAtPosition = this.antlrService.getBlockAtPosition.bind(this.antlrService)
this.getCurrentFileAST = this.antlrService.getCurrentFileAST.bind(this.antlrService)
this.on('editor', 'didChangeFile', async (file) => {
console.log('contentChanged', file)
@ -143,6 +145,7 @@ export class CodeParser extends Plugin {
}
}
}
// NODE HELPERS
@ -512,17 +515,19 @@ export class CodeParser extends Plugin {
* @returns
*/
async getVariableDeclaration(node: any) {
const nodeVisibility = node.visibility && node.visibility.length ? node.visibility + ' ' : ''
const nodeName = node.name && node.name.length ? node.name : ''
if (node.typeDescriptions && node.typeDescriptions.typeString) {
return `${node.typeDescriptions.typeString} ${node.visibility}${node.name && node.name.length ? ` ${node.name}` : ''}`
return `${node.typeDescriptions.typeString} ${nodeVisibility}${nodeName}`
} else {
if (node.typeName && node.typeName.name) {
return `${node.typeName.name} ${node.visibility}${node.name && node.name.length ? ` ${node.name}` : ''}`
return `${node.typeName.name} ${nodeVisibility}${nodeName}`
}
else if (node.typeName && node.typeName.namePath) {
return `${node.typeName.namePath} ${node.visibility}${node.name && node.name.length ? ` ${node.name}` : ''}`
return `${node.typeName.namePath} ${nodeVisibility}${nodeName}`
}
else {
return `${node.visibility}${node.name && node.name.length ? ` ${node.name}` : ''}`
return `${nodeName}${nodeName}`
}
}
}

@ -31,15 +31,17 @@ export default class CodeParserAntlrService {
*/
async getCurrentFileAST(text: string | null = null) {
this.plugin.currentFile = await this.plugin.call('fileManager', 'file')
if (!this.plugin.currentFile) return
const fileContent = text || await this.plugin.call('fileManager', 'readFile', this.plugin.currentFile)
try {
const ast = await this.parseSolidity(fileContent)
this.plugin.currentFileAST = ast
} catch (e) {
console.log(e)
if(this.plugin.currentFile && this.plugin.currentFile.endsWith('.sol')) {
if (!this.plugin.currentFile) return
const fileContent = text || await this.plugin.call('fileManager', 'readFile', this.plugin.currentFile)
try {
const ast = await this.parseSolidity(fileContent)
this.plugin.currentFileAST = ast
} catch (e) {
console.log(e)
}
return this.plugin.currentFileAST
}
return this.plugin.currentFileAST
}
/**
@ -84,7 +86,14 @@ export default class CodeParserAntlrService {
},
InvalidNode: function (node) {
nodes.push({ ...node, nodeType: node.type })
},
EnumDefinition: function (node) {
nodes.push({ ...node, nodeType: node.type })
},
StructDefinition: function (node) {
nodes.push({ ...node, nodeType: node.type })
}
})
console.log("LIST NODES", nodes)
return nodes
@ -117,8 +126,8 @@ export default class CodeParserAntlrService {
}
})
if (lastNode && lastNode.expression && lastNode.expression.expression) {
console.log('lastNode with expression', lastNode, lastNode.expression)
return lastNode.expression.expression
console.log('lastNode with expression', lastNode, lastNode.expression, lastNode.expression.expression)
// return lastNode.expression.expression
}
if (lastNode && lastNode.expression) {
console.log('lastNode with expression', lastNode, lastNode.expression)
@ -138,9 +147,7 @@ export default class CodeParserAntlrService {
async getBlockAtPosition(position: any, text: string = null) {
await this.getCurrentFileAST(text)
const allowedTypes = ['SourceUnit', 'ContractDefinition', 'FunctionDefinition']
const walkAst = (node) => {
console.log(node)
if (node.loc.start.line <= position.lineNumber && node.loc.end.line >= position.lineNumber) {
const children = node.children || node.subNodes
if (children && allowedTypes.indexOf(node.type) !== -1) {

@ -2,10 +2,10 @@
import { CompilerAbstract } from '@remix-project/remix-solidity'
import { Compiler } from '@remix-project/remix-solidity'
import { AstNode, CompilationError, CompilationResult, CompilationSource } from '@remix-project/remix-solidity'
import { helper } from '@remix-project/remix-solidity'
import { CompilationError, CompilationResult, CompilationSource } from '@remix-project/remix-solidity'
import { CodeParser } from "../code-parser";
import { fileDecoration, fileDecorationType } from '@remix-ui/file-decorators'
import { sourceMappingDecoder } from '@remix-project/remix-debug'
export default class CodeParserCompiler {
plugin: CodeParser
@ -22,7 +22,9 @@ export default class CodeParserCompiler {
init() {
this.onAstFinished = async (success, data: CompilationResult, source: CompilationSource, input: any, version) => {
console.log('compile success', success, data, this)
console.log('compile success', success, data)
this.plugin.call('editor', 'clearAnnotations')
this.errorState = true
const checkIfFatalError = (error: CompilationError) => {
@ -40,104 +42,20 @@ export default class CodeParserCompiler {
if (data.errors) {
const sources = result.getSourceCode().sources
for (const error of data.errors) {
const pos = helper.getPositionDetails(error.formattedMessage)
const filePosition = Object.keys(sources).findIndex((fileName) => fileName === error.sourceLocation.file)
const lineColumn = await this.plugin.call('offsetToLineColumnConverter', 'offsetToLineColumn',
{
start: error.sourceLocation.start,
length: error.sourceLocation.end - error.sourceLocation.start
},
filePosition,
result.getSourceCode().sources,
null)
allErrors.push({ error, lineColumn })
}
console.log('allErrors', allErrors)
await this.plugin.call('editor', 'addErrorMarker', allErrors)
const errorsPerFiles = {}
for (const error of allErrors) {
if (!errorsPerFiles[error.error.sourceLocation.file]) {
errorsPerFiles[error.error.sourceLocation.file] = []
}
errorsPerFiles[error.error.sourceLocation.file].push(error.error)
}
const errorPriority = {
'error': 0,
'warning': 1,
}
const lineBreaks = sourceMappingDecoder.getLinebreakPositions(sources[error.sourceLocation.file].content)
const lineColumn = sourceMappingDecoder.convertOffsetToLineColumn({
start: error.sourceLocation.start,
length: error.sourceLocation.end - error.sourceLocation.start
}, lineBreaks)
// sort errorPerFiles by error priority
const sortedErrorsPerFiles = {}
for (const fileName in errorsPerFiles) {
const errors = errorsPerFiles[fileName]
errors.sort((a, b) => {
return errorPriority[a.severity] - errorPriority[b.severity]
}
)
sortedErrorsPerFiles[fileName] = errors
}
console.log('sortedErrorsPerFiles', sortedErrorsPerFiles)
const filesWithOutErrors = Object.keys(sources).filter((fileName) => !sortedErrorsPerFiles[fileName])
console.log('filesWithOutErrors', filesWithOutErrors)
// add decorators
const decorators: fileDecoration[] = []
for (const fileName in sortedErrorsPerFiles) {
const errors = sortedErrorsPerFiles[fileName]
const decorator: fileDecoration = {
path: fileName,
isDirectory: false,
fileStateType: errors[0].severity === 'error' ? fileDecorationType.Error : fileDecorationType.Warning,
fileStateLabelClass: errors[0].severity === 'error' ? 'text-danger' : 'text-warning',
fileStateIconClass: '',
fileStateIcon: '',
text: errors.length,
owner: 'code-parser',
bubble: true,
commment: errors.map((error) => error.message),
}
decorators.push(decorator)
}
for (const fileName of filesWithOutErrors) {
const decorator: fileDecoration = {
path: fileName,
isDirectory: false,
fileStateType: fileDecorationType.None,
fileStateLabelClass: '',
fileStateIconClass: '',
fileStateIcon: '',
text: '',
owner: 'code-parser',
bubble: false
}
decorators.push(decorator)
allErrors.push({ error, lineColumn })
}
console.log(decorators)
await this.plugin.call('fileDecorator', 'setFileDecorators', decorators)
await this.plugin.call('editor', 'clearErrorMarkers', filesWithOutErrors)
await this.plugin.call('editor', 'addErrorMarker', allErrors)
this.addDecorators(allErrors, sources)
} else {
await this.plugin.call('editor', 'clearErrorMarkers', result.getSourceCode().sources)
const decorators: fileDecoration[] = []
for (const fileName of Object.keys(result.getSourceCode().sources)) {
const decorator: fileDecoration = {
path: fileName,
isDirectory: false,
fileStateType: fileDecorationType.None,
fileStateLabelClass: '',
fileStateIconClass: '',
fileStateIcon: '',
text: '',
owner: 'code-parser',
bubble: false
}
decorators.push(decorator)
}
console.log(decorators)
await this.plugin.call('fileDecorator', 'setFileDecorators', decorators)
await this.clearDecorators(result.getSourceCode().sources)
}
@ -170,29 +88,136 @@ export default class CodeParserCompiler {
this.compiler.event.register('compilationFinished', this.onAstFinished)
}
// COMPILER
// COMPILER
/**
*
* @returns
*/
async compile() {
async compile() {
try {
const state = await this.plugin.call('solidity', 'getCompilerState')
this.compiler.set('optimize', state.optimize)
this.compiler.set('evmVersion', state.evmVersion)
this.compiler.set('language', state.language)
this.compiler.set('runs', state.runs)
this.compiler.set('useFileConfiguration', state.useFileConfiguration)
this.plugin.currentFile = await this.plugin.call('fileManager', 'file')
console.log(this.plugin.currentFile)
if (!this.plugin.currentFile) return
const content = await this.plugin.call('fileManager', 'readFile', this.plugin.currentFile)
const sources = { [this.plugin.currentFile]: { content } }
this.compiler.compile(sources, this.plugin.currentFile)
if (this.plugin.currentFile && this.plugin.currentFile.endsWith('.sol')) {
const state = await this.plugin.call('solidity', 'getCompilerState')
console.log('COMPILER STATE', state)
this.compiler.set('optimize', state.optimize)
this.compiler.set('evmVersion', state.evmVersion)
this.compiler.set('language', state.language)
this.compiler.set('runs', state.runs)
this.compiler.set('useFileConfiguration', true)
const configFileContent = {
"language": "Solidity",
"settings": {
"optimizer": {
"enabled": false,
"runs": 200
},
"outputSelection": {
"*": {
"": ["ast"],
"*": ["evm.gasEstimates"]
}
},
"evmVersion": state.evmVersion && state.evmVersion.toString() || "byzantium",
}
}
this.compiler.set('configFileContent', JSON.stringify(configFileContent))
this.plugin.currentFile = await this.plugin.call('fileManager', 'file')
console.log(this.plugin.currentFile)
if (!this.plugin.currentFile) return
const content = await this.plugin.call('fileManager', 'readFile', this.plugin.currentFile)
const sources = { [this.plugin.currentFile]: { content } }
this.compiler.compile(sources, this.plugin.currentFile)
}
} catch (e) {
console.log(e)
}
}
async addDecorators(allErrors: any[], sources: any) {
const errorsPerFiles = {}
for (const error of allErrors) {
if (!errorsPerFiles[error.error.sourceLocation.file]) {
errorsPerFiles[error.error.sourceLocation.file] = []
}
errorsPerFiles[error.error.sourceLocation.file].push(error.error)
}
const errorPriority = {
'error': 0,
'warning': 1,
}
// sort errorPerFiles by error priority
const sortedErrorsPerFiles = {}
for (const fileName in errorsPerFiles) {
const errors = errorsPerFiles[fileName]
errors.sort((a, b) => {
return errorPriority[a.severity] - errorPriority[b.severity]
}
)
sortedErrorsPerFiles[fileName] = errors
}
const filesWithOutErrors = Object.keys(sources).filter((fileName) => !sortedErrorsPerFiles[fileName])
// add decorators
const decorators: fileDecoration[] = []
for (const fileName in sortedErrorsPerFiles) {
const errors = sortedErrorsPerFiles[fileName]
const decorator: fileDecoration = {
path: fileName,
isDirectory: false,
fileStateType: errors[0].severity === 'error' ? fileDecorationType.Error : fileDecorationType.Warning,
fileStateLabelClass: errors[0].severity === 'error' ? 'text-danger' : 'text-warning',
fileStateIconClass: '',
fileStateIcon: '',
text: errors.length,
owner: 'code-parser',
bubble: true,
commment: errors.map((error) => error.message),
}
decorators.push(decorator)
}
for (const fileName of filesWithOutErrors) {
const decorator: fileDecoration = {
path: fileName,
isDirectory: false,
fileStateType: fileDecorationType.None,
fileStateLabelClass: '',
fileStateIconClass: '',
fileStateIcon: '',
text: '',
owner: 'code-parser',
bubble: false
}
decorators.push(decorator)
}
await this.plugin.call('fileDecorator', 'setFileDecorators', decorators)
await this.plugin.call('editor', 'clearErrorMarkers', filesWithOutErrors)
}
async clearDecorators(sources: any) {
const decorators: fileDecoration[] = []
for (const fileName of Object.keys(sources)) {
const decorator: fileDecoration = {
path: fileName,
isDirectory: false,
fileStateType: fileDecorationType.None,
fileStateLabelClass: '',
fileStateIconClass: '',
fileStateIcon: '',
text: '',
owner: 'code-parser',
bubble: false
}
decorators.push(decorator)
}
await this.plugin.call('fileDecorator', 'setFileDecorators', decorators)
}
}

@ -30,15 +30,12 @@ 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) || !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 || 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) {

@ -48,17 +48,17 @@ export class RemixCompletionProvider implements languages.CompletionItemProvider
console.log('expression elements', expressionElements)
let dotCompleted = false
if (expressionElements.length === 2) {
const globalCompletion = getContextualAutoCompleteByGlobalVariable(lastNodeInExpression.name, range, this.monaco)
if (globalCompletion) {
dotCompleted = true
suggestions = [...suggestions, ...globalCompletion]
}
if (lastNodeInExpression.name === 'this') {
dotCompleted = true
nodes = [...nodes, ...await this.getContractCompletions(nodes, position)]
}
//if (expressionElements.length === 2) {
const globalCompletion = getContextualAutoCompleteByGlobalVariable(lastNodeInExpression.name, range, this.monaco)
if (globalCompletion) {
dotCompleted = true
suggestions = [...suggestions, ...globalCompletion]
}
if (lastNodeInExpression.name === 'this') {
dotCompleted = true
nodes = [...nodes, ...await this.getContractCompletions(nodes, position)]
}
//}
if (expressionElements.length > 1 && !dotCompleted) {
const last = lastNodeInExpression.name || lastNodeInExpression.memberName
@ -72,7 +72,7 @@ export class RemixCompletionProvider implements languages.CompletionItemProvider
if (block) {
nodesAtPosition = await this.props.plugin.call('codeParser', 'nodesAtPosition', block.body ? block.body.range[0] : block.range[0])
console.log('NODES AT POSITION WITH BLOCK', nodesAtPosition)
} else{
} else {
nodesAtPosition = await this.props.plugin.call('codeParser', 'nodesAtPosition', cursorPosition)
console.log('NODES AT POSITION', nodesAtPosition)
}
@ -87,7 +87,7 @@ export class RemixCompletionProvider implements languages.CompletionItemProvider
console.log('FOUND NODE', nodeOfScope)
if (nodeOfScope.typeName && nodeOfScope.typeName.nodeType === 'UserDefinedTypeName') {
const declarationOf = await this.props.plugin.call('codeParser', 'declarationOf', nodeOfScope.typeName)
console.log('HAS DECLARATION OF', declarationOf)
console.log('METHOD 1 HAS DECLARATION OF', declarationOf)
nodes = [...nodes, ...declarationOf.nodes || declarationOf.members]
const baseContracts = await this.getlinearizedBaseContracts(declarationOf)
for (const baseContract of baseContracts) {
@ -98,26 +98,26 @@ export class RemixCompletionProvider implements languages.CompletionItemProvider
}
}
// anything within the block statements might provide a clue to what it is
if (!nodes.length) {
for (const node of nodesAtPosition) {
if (node.statements) {
for (const statement of node.statements) {
if (statement.expression && statement.expression.memberName === last) {
const declarationOf = await this.props.plugin.call('codeParser', 'declarationOf', statement.expression)
if (declarationOf.typeName && declarationOf.typeName.nodeType === 'UserDefinedTypeName') {
const baseDeclaration = await this.props.plugin.call('codeParser', 'declarationOf', declarationOf.typeName)
console.log('HAS BASE DECLARATION OF', baseDeclaration)
nodes = [...nodes, ...baseDeclaration.nodes || baseDeclaration.members]
}
// if (!nodes.length) {
for (const node of nodesAtPosition) {
if (node.statements) {
for (const statement of node.statements) {
if (statement.expression && statement.expression.memberName === last) {
const declarationOf = await this.props.plugin.call('codeParser', 'declarationOf', statement.expression)
if (declarationOf.typeName && declarationOf.typeName.nodeType === 'UserDefinedTypeName') {
const baseDeclaration = await this.props.plugin.call('codeParser', 'declarationOf', declarationOf.typeName)
console.log('METHOD 2 HAS BASE DECLARATION OF', baseDeclaration)
nodes = [...nodes, ...baseDeclaration.nodes || baseDeclaration.members]
}
}
}
}
}
}
// }
// brute force search in all nodes with the name
if (!nodes.length) {
if (!nodes.length || 1) {
const nodesOfScope = await this.props.plugin.call('codeParser', 'getNodesWithName', last)
console.log('NODES WITHE NAME ', last, nodesOfScope)
for (const nodeOfScope of nodesOfScope) {
@ -125,11 +125,11 @@ export class RemixCompletionProvider implements languages.CompletionItemProvider
console.log('FOUND NODE', nodeOfScope)
if (nodeOfScope.typeName && nodeOfScope.typeName.nodeType === 'UserDefinedTypeName') {
const declarationOf = await this.props.plugin.call('codeParser', 'declarationOf', nodeOfScope.typeName)
console.log('HAS DECLARATION OF', declarationOf)
// nodes = [...nodes, ...declarationOf.nodes || declarationOf.members]
console.log('METHOD 3 HAS DECLARATION OF', declarationOf)
nodes = [...nodes, ...declarationOf.nodes || declarationOf.members]
//const baseContracts = await this.getlinearizedBaseContracts(declarationOf)
//for (const baseContract of baseContracts) {
//nodes = [...nodes, ...baseContract.nodes]
//nodes = [...nodes, ...baseContract.nodes]
//}
}
}
@ -153,6 +153,18 @@ export class RemixCompletionProvider implements languages.CompletionItemProvider
console.log('WORD', word, wordAt)
console.log('NODES', nodes)
// remove duplicates
const nodeIds = {};
const filteredNodes = nodes.filter((node) => {
if (node.id) {
if (nodeIds[node.id]) {
return false;
}
nodeIds[node.id] = true;
}
return true;
});
const getNodeLink = async (node: any) => {
return await this.props.plugin.call('codeParser', 'getNodeLink', node)
}
@ -166,7 +178,7 @@ export class RemixCompletionProvider implements languages.CompletionItemProvider
}
const completeParameters = async (parameters: any) => {
const localParam = ( parameters && parameters.parameters ) || (parameters)
const localParam = (parameters && parameters.parameters) || (parameters)
if (localParam) {
const params = []
for (const key in localParam) {
@ -178,11 +190,18 @@ export class RemixCompletionProvider implements languages.CompletionItemProvider
const getVariableDeclaration = async (node: any) => {
return await this.props.plugin.call('codeParser', 'getVariableDeclaration', node)
let variableDeclaration = await this.props.plugin.call('codeParser', 'getVariableDeclaration', node)
if (node.scope) {
const scopeNode = await this.props.plugin.call('codeParser', 'getNodeById', node.scope)
if (scopeNode) {
variableDeclaration = `${scopeNode.name}.${variableDeclaration}`
}
}
return variableDeclaration
}
for (const node of Object.values(nodes) as any[]) {
for (const node of Object.values(filteredNodes) as any[]) {
if (!node.name) continue
if (node.nodeType === 'VariableDeclaration') {
const completion = {
@ -327,7 +346,7 @@ export class RemixCompletionProvider implements languages.CompletionItemProvider
*/
private async getLastNodeInExpression(lineTextBeforeCursor: string) {
const wrapLineInFunction = async(text: string) => {
const wrapLineInFunction = async (text: string) => {
return `function() {
${text}
}`
@ -351,11 +370,11 @@ export class RemixCompletionProvider implements languages.CompletionItemProvider
try {
const lineAst = await this.props.plugin.call('codeParser', 'parseSolidity', line)
const lastNode = await this.props.plugin.call('codeParser', 'getLastNodeInLine', lineAst)
if(lastNode) {
if (lastNode) {
lastNodeInExpression = lastNode
break
}
} catch (e) {
}

@ -429,7 +429,6 @@ export const EditorUI = (props: EditorUIProps) => {
props.editorAPI.clearErrorMarkers = async (sources: any) => {
if (sources) {
console.log('clear', sources)
for (const source of (Array.isArray(sources) ? sources : Object.keys(sources))) {
const filePath = source
const model = editorModelsState[filePath]?.model

Loading…
Cancel
Save