|
|
|
@ -28,15 +28,15 @@ export default class CodeParserAntlrService { |
|
|
|
|
parserThreshold: number = 10 |
|
|
|
|
parserThresholdSampleAmount = 3 |
|
|
|
|
cache: { |
|
|
|
|
[name: string]: { |
|
|
|
|
text: string |
|
|
|
|
ast: antlr.ParseResult | null |
|
|
|
|
duration?: number |
|
|
|
|
parsingEnabled?: boolean |
|
|
|
|
blocks?: BlockDefinition[] |
|
|
|
|
blockDurations?: number[] |
|
|
|
|
} |
|
|
|
|
} = {} |
|
|
|
|
[name: string]: { |
|
|
|
|
text: string, |
|
|
|
|
ast: antlr.ParseResult | null, |
|
|
|
|
duration?: number, |
|
|
|
|
parsingEnabled?: boolean, |
|
|
|
|
blocks?: BlockDefinition[], |
|
|
|
|
blockDurations?: number[] |
|
|
|
|
} |
|
|
|
|
} = {}; |
|
|
|
|
constructor(plugin: CodeParser) { |
|
|
|
|
this.plugin = plugin |
|
|
|
|
this.createWorker() |
|
|
|
@ -52,23 +52,22 @@ export default class CodeParserAntlrService { |
|
|
|
|
|
|
|
|
|
this.worker.addEventListener('message', function (ev) { |
|
|
|
|
switch (ev.data.cmd) { |
|
|
|
|
case 'parsed': |
|
|
|
|
if (ev.data.ast && self.parserStartTime === ev.data.timestamp) { |
|
|
|
|
self.cache[ev.data.file] = { |
|
|
|
|
...self.cache[ev.data.file], |
|
|
|
|
text: ev.data.text, |
|
|
|
|
ast: ev.data.ast, |
|
|
|
|
duration: ev.data.duration, |
|
|
|
|
blocks: ev.data.blocks, |
|
|
|
|
blockDurations: self.cache[ev.data.file].blockDurations |
|
|
|
|
? [...self.cache[ev.data.file].blockDurations.slice(-self.parserThresholdSampleAmount), ev.data.blockDuration] |
|
|
|
|
: [ev.data.blockDuration], |
|
|
|
|
} |
|
|
|
|
self.setFileParsingState(ev.data.file) |
|
|
|
|
case 'parsed': |
|
|
|
|
if (ev.data.ast && self.parserStartTime === ev.data.timestamp) { |
|
|
|
|
self.cache[ev.data.file] = { |
|
|
|
|
...self.cache[ev.data.file], |
|
|
|
|
text: ev.data.text, |
|
|
|
|
ast: ev.data.ast, |
|
|
|
|
duration: ev.data.duration, |
|
|
|
|
blocks: ev.data.blocks, |
|
|
|
|
blockDurations: self.cache[ev.data.file].blockDurations? [...self.cache[ev.data.file].blockDurations.slice(-self.parserThresholdSampleAmount), ev.data.blockDuration]: [ev.data.blockDuration] |
|
|
|
|
} |
|
|
|
|
break |
|
|
|
|
self.setFileParsingState(ev.data.file) |
|
|
|
|
} |
|
|
|
|
break; |
|
|
|
|
} |
|
|
|
|
}) |
|
|
|
|
|
|
|
|
|
}); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
setFileParsingState(file: string) { |
|
|
|
@ -105,8 +104,9 @@ export default class CodeParserAntlrService { |
|
|
|
|
text, |
|
|
|
|
timestamp: this.parserStartTime, |
|
|
|
|
file, |
|
|
|
|
parsingEnabled: (this.cache[file] && this.cache[file].parsingEnabled) || true, |
|
|
|
|
}) |
|
|
|
|
parsingEnabled: (this.cache[file] && this.cache[file].parsingEnabled) || true |
|
|
|
|
}); |
|
|
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
async parseSolidity(text: string) { |
|
|
|
@ -115,22 +115,22 @@ export default class CodeParserAntlrService { |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
* Tries to parse the current file or the given text and returns the AST |
|
|
|
|
* If the parsing fails it returns the last successful AST for this file |
|
|
|
|
* @param text |
|
|
|
|
* @returns |
|
|
|
|
*/ |
|
|
|
|
* Tries to parse the current file or the given text and returns the AST |
|
|
|
|
* If the parsing fails it returns the last successful AST for this file |
|
|
|
|
* @param text
|
|
|
|
|
* @returns
|
|
|
|
|
*/ |
|
|
|
|
async setCurrentFileAST(text: string | null = null) { |
|
|
|
|
try { |
|
|
|
|
this.plugin.currentFile = await this.plugin.call('fileManager', 'file') |
|
|
|
|
if (this.plugin.currentFile && this.plugin.currentFile.endsWith('.sol')) { |
|
|
|
|
const fileContent = text || (await this.plugin.call('fileManager', 'readFile', this.plugin.currentFile)) |
|
|
|
|
const fileContent = text || await this.plugin.call('fileManager', 'readFile', this.plugin.currentFile) |
|
|
|
|
if (!this.cache[this.plugin.currentFile]) { |
|
|
|
|
this.cache[this.plugin.currentFile] = { |
|
|
|
|
text: '', |
|
|
|
|
ast: null, |
|
|
|
|
parsingEnabled: true, |
|
|
|
|
blockDurations: [], |
|
|
|
|
blockDurations: [] |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
if (this.cache[this.plugin.currentFile] && this.cache[this.plugin.currentFile].text !== fileContent) { |
|
|
|
@ -147,15 +147,15 @@ export default class CodeParserAntlrService { |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
* Lists the AST nodes from the current file parser |
|
|
|
|
* These nodes need to be changed to match the node types returned by the compiler |
|
|
|
|
* @returns |
|
|
|
|
*/ |
|
|
|
|
* Lists the AST nodes from the current file parser |
|
|
|
|
* These nodes need to be changed to match the node types returned by the compiler |
|
|
|
|
* @returns
|
|
|
|
|
*/ |
|
|
|
|
async listAstNodes() { |
|
|
|
|
this.plugin.currentFile = await this.plugin.call('fileManager', 'file') |
|
|
|
|
if (!this.cache[this.plugin.currentFile]) return |
|
|
|
|
const nodes: AstNode[] = [] |
|
|
|
|
;(SolidityParser as any).visit(this.cache[this.plugin.currentFile].ast, { |
|
|
|
|
const nodes: AstNode[] = []; |
|
|
|
|
(SolidityParser as any).visit(this.cache[this.plugin.currentFile].ast, { |
|
|
|
|
StateVariableDeclaration: (node: antlr.StateVariableDeclaration) => { |
|
|
|
|
if (node.variables) { |
|
|
|
|
for (const variable of node.variables) { |
|
|
|
@ -195,16 +195,18 @@ export default class CodeParserAntlrService { |
|
|
|
|
}, |
|
|
|
|
StructDefinition: function (node: antlr.StructDefinition) { |
|
|
|
|
nodes.push({ ...node, nodeType: node.type, id: null, src: null }) |
|
|
|
|
}, |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
}) |
|
|
|
|
return nodes |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
* |
|
|
|
|
* @param ast |
|
|
|
|
* @returns |
|
|
|
|
*/ |
|
|
|
|
*
|
|
|
|
|
* @param ast
|
|
|
|
|
* @returns
|
|
|
|
|
*/ |
|
|
|
|
async getLastNodeInLine(ast: string) { |
|
|
|
|
let lastNode: any |
|
|
|
|
const checkLastNode = (node: antlr.MemberAccess | antlr.Identifier) => { |
|
|
|
@ -217,13 +219,13 @@ export default class CodeParserAntlrService { |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
;(SolidityParser as any).visit(ast, { |
|
|
|
|
(SolidityParser as any).visit(ast, { |
|
|
|
|
MemberAccess: function (node: antlr.MemberAccess) { |
|
|
|
|
checkLastNode(node) |
|
|
|
|
}, |
|
|
|
|
Identifier: function (node: antlr.Identifier) { |
|
|
|
|
checkLastNode(node) |
|
|
|
|
}, |
|
|
|
|
} |
|
|
|
|
}) |
|
|
|
|
if (lastNode && lastNode.expression) { |
|
|
|
|
return lastNode.expression |
|
|
|
@ -231,8 +233,8 @@ export default class CodeParserAntlrService { |
|
|
|
|
return lastNode |
|
|
|
|
} |
|
|
|
|
/* |
|
|
|
|
* get the code blocks of the current file |
|
|
|
|
*/ |
|
|
|
|
* get the code blocks of the current file |
|
|
|
|
*/ |
|
|
|
|
async getCurrentFileBlocks(text: string | null = null) { |
|
|
|
|
this.plugin.currentFile = await this.plugin.call('fileManager', 'file') |
|
|
|
|
if (this.cache[this.plugin.currentFile]) { |
|
|
|
@ -241,15 +243,12 @@ export default class CodeParserAntlrService { |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
if (this.plugin.currentFile && this.plugin.currentFile.endsWith('.sol')) { |
|
|
|
|
const fileContent = text || (await this.plugin.call('fileManager', 'readFile', this.plugin.currentFile)) |
|
|
|
|
const fileContent = text || await this.plugin.call('fileManager', 'readFile', this.plugin.currentFile) |
|
|
|
|
try { |
|
|
|
|
const startTime = Date.now() |
|
|
|
|
const blocks = (SolidityParser as any).parseBlock(fileContent, { loc: true, range: true, tolerant: true }) |
|
|
|
|
if (this.cache[this.plugin.currentFile] && this.cache[this.plugin.currentFile].blockDurations) { |
|
|
|
|
this.cache[this.plugin.currentFile].blockDurations = [ |
|
|
|
|
...this.cache[this.plugin.currentFile].blockDurations.slice(-this.parserThresholdSampleAmount), |
|
|
|
|
Date.now() - startTime, |
|
|
|
|
] |
|
|
|
|
if(this.cache[this.plugin.currentFile] && this.cache[this.plugin.currentFile].blockDurations){ |
|
|
|
|
this.cache[this.plugin.currentFile].blockDurations = [...this.cache[this.plugin.currentFile].blockDurations.slice(-this.parserThresholdSampleAmount), Date.now() - startTime] |
|
|
|
|
this.setFileParsingState(this.plugin.currentFile) |
|
|
|
|
} |
|
|
|
|
if (blocks) this.cache[this.plugin.currentFile].blocks = blocks |
|
|
|
@ -261,12 +260,12 @@ export default class CodeParserAntlrService { |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
* Returns the block surrounding the given position |
|
|
|
|
* For example if the position is in the middle of a function, it will return the function |
|
|
|
|
* @param {position} position |
|
|
|
|
* @param {string} text // optional
|
|
|
|
|
* @return {any} |
|
|
|
|
* */ |
|
|
|
|
* Returns the block surrounding the given position |
|
|
|
|
* For example if the position is in the middle of a function, it will return the function |
|
|
|
|
* @param {position} position |
|
|
|
|
* @param {string} text // optional
|
|
|
|
|
* @return {any} |
|
|
|
|
* */ |
|
|
|
|
async getANTLRBlockAtPosition(position: any, text: string = null) { |
|
|
|
|
const blocks: any[] = await this.getCurrentFileBlocks(text) |
|
|
|
|
|
|
|
|
|