|
|
|
@ -1,3 +1,4 @@ |
|
|
|
|
import { isArray } from "lodash" |
|
|
|
|
import { editor, languages, Position } from "monaco-editor" |
|
|
|
|
import monaco from "../../types/monaco" |
|
|
|
|
import { EditorUIProps } from "../remix-ui-editor" |
|
|
|
@ -39,114 +40,53 @@ export class RemixCompletionProvider implements languages.CompletionItemProvider |
|
|
|
|
if (context.triggerCharacter === '.') { |
|
|
|
|
console.clear() |
|
|
|
|
console.log('TEXT', line) |
|
|
|
|
const textBeforeCursor = line.substring(0, position.column - 1) |
|
|
|
|
const textAfterCursor = line.substring(position.column - 1) |
|
|
|
|
// parse the line witout changing the line
|
|
|
|
|
|
|
|
|
|
const wrapLineInFunction = (text: string) => { |
|
|
|
|
return `function() {
|
|
|
|
|
${text} |
|
|
|
|
}` |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
let lastNode |
|
|
|
|
const checkLastNode = (node) => { |
|
|
|
|
if (!node) return |
|
|
|
|
if (lastNode && lastNode.range && lastNode.range[1]) { |
|
|
|
|
if (node.range[1] > lastNode.range[1]) { |
|
|
|
|
lastNode = node |
|
|
|
|
} |
|
|
|
|
} else { |
|
|
|
|
lastNode = node |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
const lineTextBeforeCursor = line.substring(0, position.column - 1) |
|
|
|
|
const lastNodeInExpression = await this.getLastNodeInExpression(lineTextBeforeCursor) |
|
|
|
|
console.log('lastNode found', lastNodeInExpression) |
|
|
|
|
console.log(lineTextBeforeCursor) |
|
|
|
|
const expressionElements = lineTextBeforeCursor.split('.') |
|
|
|
|
|
|
|
|
|
const linesToCheck = |
|
|
|
|
[ |
|
|
|
|
textBeforeCursor.substring(0, textBeforeCursor.lastIndexOf('.')) + ".lastnode;", |
|
|
|
|
textBeforeCursor.substring(0, textBeforeCursor.lastIndexOf('.')) + ".lastnode;}", |
|
|
|
|
textBeforeCursor.substring(0, textBeforeCursor.lastIndexOf('.')) + ".lastnode);", |
|
|
|
|
wrapLineInFunction(textBeforeCursor.substring(0, textBeforeCursor.lastIndexOf('.')) + ".lastnode;"), |
|
|
|
|
wrapLineInFunction(textBeforeCursor.substring(0, textBeforeCursor.lastIndexOf('.')) + ".lastnode;}"), |
|
|
|
|
wrapLineInFunction(textBeforeCursor.substring(0, textBeforeCursor.lastIndexOf('.')) + ".lastnode;)"), |
|
|
|
|
] |
|
|
|
|
|
|
|
|
|
for (const line of linesToCheck) { |
|
|
|
|
try { |
|
|
|
|
const lineAst = await this.props.plugin.call('contextualListener', 'parseSource', line) |
|
|
|
|
const lastNode = await this.props.plugin.call('contextualListener', 'getLastNodeInLine', lineAst) |
|
|
|
|
checkLastNode(lastNode) |
|
|
|
|
} catch (e) { |
|
|
|
|
|
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
console.log('lastNode found', lastNode) |
|
|
|
|
console.log(textBeforeCursor, textAfterCursor) |
|
|
|
|
const splits = textBeforeCursor.split('.') |
|
|
|
|
|
|
|
|
|
console.log('splits', splits) |
|
|
|
|
console.log('expression elements', expressionElements) |
|
|
|
|
let dotCompleted = false |
|
|
|
|
if (splits.length === 2) { |
|
|
|
|
let globalCompletion = getContextualAutoCompleteByGlobalVariable(lastNode.name, range, this.monaco) |
|
|
|
|
if (expressionElements.length === 2) { |
|
|
|
|
let globalCompletion = getContextualAutoCompleteByGlobalVariable(lastNodeInExpression.name, range, this.monaco) |
|
|
|
|
if (globalCompletion) { |
|
|
|
|
dotCompleted = true |
|
|
|
|
suggestions = [...suggestions, ...globalCompletion] |
|
|
|
|
} |
|
|
|
|
if (lastNode.name === 'this') { |
|
|
|
|
if (lastNodeInExpression.name === 'this') { |
|
|
|
|
dotCompleted = true |
|
|
|
|
nodes = [...nodes, ...await this.getContractCompletions(nodes, position)] |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
if (splits.length > 1 && !dotCompleted) { |
|
|
|
|
let last = splits[splits.length - 2].trim() |
|
|
|
|
const lastParentheses = last.lastIndexOf('(') |
|
|
|
|
const lastClosingParentheses = last.lastIndexOf(')') |
|
|
|
|
const lastBracket = last.lastIndexOf('{') |
|
|
|
|
const lastSemiColon = last.lastIndexOf(';') |
|
|
|
|
let textBefore = null |
|
|
|
|
let lineWithoutEdits = null |
|
|
|
|
// get word before last closing parentheses
|
|
|
|
|
if (lastParentheses > -1 && lastClosingParentheses > -1) { |
|
|
|
|
//textBefore = last.substring(0, lastParentheses)
|
|
|
|
|
} |
|
|
|
|
// find largest
|
|
|
|
|
const lastIndex = Math.max(lastParentheses, lastBracket, lastSemiColon) |
|
|
|
|
if (lastIndex > -1) { |
|
|
|
|
textBefore = last.substring(0, lastIndex + 1) |
|
|
|
|
console.log('textBefore', textBefore) |
|
|
|
|
console.log('text without edits', textBefore, textAfterCursor) |
|
|
|
|
lineWithoutEdits = `${textBefore}${textAfterCursor}` |
|
|
|
|
} |
|
|
|
|
last = lastNode.name || lastNode.memberName |
|
|
|
|
console.log('last', last) |
|
|
|
|
|
|
|
|
|
const lines = model.getLinesContent() |
|
|
|
|
lines[position.lineNumber - 1] = lineWithoutEdits |
|
|
|
|
if (expressionElements.length > 1 && !dotCompleted) { |
|
|
|
|
|
|
|
|
|
const textWithoutEdits = lines.join('\n') |
|
|
|
|
console.log('textWithoutEdits', textWithoutEdits) |
|
|
|
|
const last = lastNodeInExpression.name || lastNodeInExpression.memberName |
|
|
|
|
console.log('last', last) |
|
|
|
|
|
|
|
|
|
let nodesAtPosition = await this.props.plugin.call('contextualListener', 'nodesAtEditorPosition', cursorPosition) |
|
|
|
|
let nodesAtPosition; |
|
|
|
|
|
|
|
|
|
console.log('NODES AT POSITION', nodesAtPosition) |
|
|
|
|
const block = await this.props.plugin.call('contextualListener', 'getBlockName', position, textWithoutEdits) |
|
|
|
|
const block = await this.props.plugin.call('codeParser', 'getBlockAtPosition', position, null) |
|
|
|
|
console.log('BLOCK', block) |
|
|
|
|
//if (!nodesAtPosition.length) {
|
|
|
|
|
|
|
|
|
|
if (block) { |
|
|
|
|
nodesAtPosition = await this.props.plugin.call('contextualListener', 'nodesAtEditorPosition', block.body ? block.body.range[0] : block.range[0]) |
|
|
|
|
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{ |
|
|
|
|
nodesAtPosition = await this.props.plugin.call('codeParser', 'nodesAtPosition', cursorPosition) |
|
|
|
|
console.log('NODES AT POSITION', nodesAtPosition) |
|
|
|
|
} |
|
|
|
|
//}
|
|
|
|
|
|
|
|
|
|
// explore nodes at the BLOCK
|
|
|
|
|
if (nodesAtPosition) { |
|
|
|
|
for (const node of nodesAtPosition) { |
|
|
|
|
const nodesOfScope = await this.props.plugin.call('contextualListener', 'nodesWithScope', node.id) |
|
|
|
|
const nodesOfScope = await this.props.plugin.call('codeParser', 'getNodesWithScope', node.id) |
|
|
|
|
console.log('NODES OF SCOPE ', node.name, node.id, nodesOfScope) |
|
|
|
|
for (const nodeOfScope of nodesOfScope) { |
|
|
|
|
if (nodeOfScope.name === last) { |
|
|
|
|
console.log('FOUND NODE', nodeOfScope) |
|
|
|
|
if (nodeOfScope.typeName && nodeOfScope.typeName.nodeType === 'UserDefinedTypeName') { |
|
|
|
|
const declarationOf = await this.props.plugin.call('contextualListener', 'declarationOf', nodeOfScope.typeName) |
|
|
|
|
const declarationOf = await this.props.plugin.call('codeParser', 'declarationOf', nodeOfScope.typeName) |
|
|
|
|
console.log('HAS DECLARATION OF', declarationOf) |
|
|
|
|
nodes = [...nodes, ...declarationOf.nodes || declarationOf.members] |
|
|
|
|
const baseContracts = await this.getlinearizedBaseContracts(declarationOf) |
|
|
|
@ -163,9 +103,9 @@ export class RemixCompletionProvider implements languages.CompletionItemProvider |
|
|
|
|
if (node.statements) { |
|
|
|
|
for (const statement of node.statements) { |
|
|
|
|
if (statement.expression && statement.expression.memberName === last) { |
|
|
|
|
const declarationOf = await this.props.plugin.call('contextualListener', 'declarationOf', statement.expression) |
|
|
|
|
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('contextualListener', 'declarationOf', declarationOf.typeName) |
|
|
|
|
const baseDeclaration = await this.props.plugin.call('codeParser', 'declarationOf', declarationOf.typeName) |
|
|
|
|
console.log('HAS BASE DECLARATION OF', baseDeclaration) |
|
|
|
|
nodes = [...nodes, ...baseDeclaration.nodes || baseDeclaration.members] |
|
|
|
|
} |
|
|
|
@ -178,13 +118,13 @@ export class RemixCompletionProvider implements languages.CompletionItemProvider |
|
|
|
|
|
|
|
|
|
// brute force search in all nodes with the name
|
|
|
|
|
if (!nodes.length) { |
|
|
|
|
const nodesOfScope = await this.props.plugin.call('contextualListener', 'nodesWithName', last) |
|
|
|
|
const nodesOfScope = await this.props.plugin.call('codeParser', 'getNodesWithName', last) |
|
|
|
|
console.log('NODES WITHE NAME ', last, nodesOfScope) |
|
|
|
|
for (const nodeOfScope of nodesOfScope) { |
|
|
|
|
if (nodeOfScope.name === last) { |
|
|
|
|
console.log('FOUND NODE', nodeOfScope) |
|
|
|
|
if (nodeOfScope.typeName && nodeOfScope.typeName.nodeType === 'UserDefinedTypeName') { |
|
|
|
|
const declarationOf = await this.props.plugin.call('contextualListener', 'declarationOf', nodeOfScope.typeName) |
|
|
|
|
const declarationOf = await this.props.plugin.call('codeParser', 'declarationOf', nodeOfScope.typeName) |
|
|
|
|
console.log('HAS DECLARATION OF', declarationOf) |
|
|
|
|
// nodes = [...nodes, ...declarationOf.nodes || declarationOf.members]
|
|
|
|
|
const baseContracts = await this.getlinearizedBaseContracts(declarationOf) |
|
|
|
@ -213,43 +153,24 @@ export class RemixCompletionProvider implements languages.CompletionItemProvider |
|
|
|
|
console.log('WORD', word, wordAt) |
|
|
|
|
console.log('NODES', nodes) |
|
|
|
|
|
|
|
|
|
const getLinks = async (node: any) => { |
|
|
|
|
const position = await this.props.plugin.call('contextualListener', 'positionOfDefinition', node) |
|
|
|
|
const lastCompilationResult = await this.props.plugin.call('contextualListener', 'getLastCompilationResult') |
|
|
|
|
const filename = lastCompilationResult.getSourceName(position.file) |
|
|
|
|
const lineColumn = await this.props.plugin.call('offsetToLineColumnConverter', 'offsetToLineColumn', |
|
|
|
|
position, |
|
|
|
|
position.file, |
|
|
|
|
lastCompilationResult.getSourceCode().sources, |
|
|
|
|
lastCompilationResult.getAsts()) |
|
|
|
|
return `${filename} ${lineColumn.start.line}:${lineColumn.start.column}` |
|
|
|
|
const getNodeLink = async (node: any) => { |
|
|
|
|
return await this.props.plugin.call('codeParser', 'getNodeLink', node) |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
const getDocs = async (node: any) => { |
|
|
|
|
if (node.documentation && node.documentation.text) { |
|
|
|
|
let text = '' |
|
|
|
|
node.documentation.text.split('\n').forEach(line => { |
|
|
|
|
text += `${line.trim()}\n` |
|
|
|
|
}) |
|
|
|
|
return text |
|
|
|
|
} |
|
|
|
|
return await this.props.plugin.call('codeParser', 'getNodeDocumentation', node) |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
const getParamaters = async (parameters: any) => { |
|
|
|
|
if (parameters && parameters.parameters) { |
|
|
|
|
const params = [] |
|
|
|
|
for (const param of parameters.parameters) { |
|
|
|
|
params.push(await getVariableDeclaration(param)) |
|
|
|
|
} |
|
|
|
|
return `(${params.join(', ')})` |
|
|
|
|
} |
|
|
|
|
const getParamaters = async (node: any) => { |
|
|
|
|
return await this.props.plugin.call('codeParser', 'getFunctionParamaters', node) |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
const completeParameters = async (parameters: any) => { |
|
|
|
|
if (parameters && parameters.parameters) { |
|
|
|
|
let localParam = ( parameters && parameters.parameters ) || (parameters) |
|
|
|
|
if (localParam) { |
|
|
|
|
const params = [] |
|
|
|
|
for (const key in parameters.parameters) { |
|
|
|
|
params.push('${' + (key + 1) + ':' + parameters.parameters[key].name + '}') |
|
|
|
|
for (const key in localParam) { |
|
|
|
|
params.push('${' + (key + 1) + ':' + localParam[key].name + '}') |
|
|
|
|
} |
|
|
|
|
return `(${params.join(', ')})` |
|
|
|
|
} |
|
|
|
@ -257,14 +178,7 @@ export class RemixCompletionProvider implements languages.CompletionItemProvider |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
const getVariableDeclaration = async (node: any) => { |
|
|
|
|
if (node.typeDescriptions && node.typeDescriptions.typeString) { |
|
|
|
|
return `${node.typeDescriptions.typeString}${node.name && node.name.length ? ` ${node.name}` : ''}` |
|
|
|
|
} else |
|
|
|
|
if (node.typeName && node.typeName.name) { |
|
|
|
|
return `${node.typeName.name}${node.name && node.name.length ? ` ${node.name}` : ''}` |
|
|
|
|
} else { |
|
|
|
|
return `${node.name && node.name.length ? ` ${node.name}` : ''}` |
|
|
|
|
} |
|
|
|
|
return await this.props.plugin.call('codeParser', 'getVariableDeclaration', node) |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
@ -272,7 +186,7 @@ export class RemixCompletionProvider implements languages.CompletionItemProvider |
|
|
|
|
if (!node.name) continue |
|
|
|
|
if (node.nodeType === 'VariableDeclaration') { |
|
|
|
|
const completion = { |
|
|
|
|
label: { label: `"${node.name}"`, description: await getLinks(node), detail: ` ${await getVariableDeclaration(node)}` }, |
|
|
|
|
label: { label: `"${node.name}"`, description: await getNodeLink(node), detail: ` ${await getVariableDeclaration(node)}` }, |
|
|
|
|
kind: this.monaco.languages.CompletionItemKind.Variable, |
|
|
|
|
insertText: node.name, |
|
|
|
|
range: range, |
|
|
|
@ -282,7 +196,7 @@ export class RemixCompletionProvider implements languages.CompletionItemProvider |
|
|
|
|
} else if (node.nodeType === 'FunctionDefinition') { |
|
|
|
|
|
|
|
|
|
const completion = { |
|
|
|
|
label: { label: `"${node.name}"`, description: await getLinks(node), detail: ` -> ${node.name} ${await getParamaters(node.parameters)}` }, |
|
|
|
|
label: { label: `"${node.name}"`, description: await getNodeLink(node), detail: ` -> ${node.name} ${await getParamaters(node)}` }, |
|
|
|
|
kind: this.monaco.languages.CompletionItemKind.Function, |
|
|
|
|
insertText: `${node.name}${await completeParameters(node.parameters)};`, |
|
|
|
|
insertTextRules: this.monaco.languages.CompletionItemInsertTextRule.InsertAsSnippet, |
|
|
|
@ -293,7 +207,7 @@ export class RemixCompletionProvider implements languages.CompletionItemProvider |
|
|
|
|
} else if |
|
|
|
|
(node.nodeType === 'ContractDefinition') { |
|
|
|
|
const completion = { |
|
|
|
|
label: { label: `"${node.name}"`, description: await getLinks(node), detail: ` ${node.name}` }, |
|
|
|
|
label: { label: `"${node.name}"`, description: await getNodeLink(node), detail: ` ${node.name}` }, |
|
|
|
|
kind: this.monaco.languages.CompletionItemKind.Interface, |
|
|
|
|
insertText: node.name, |
|
|
|
|
range: range, |
|
|
|
@ -303,7 +217,7 @@ export class RemixCompletionProvider implements languages.CompletionItemProvider |
|
|
|
|
} else if |
|
|
|
|
(node.nodeType === 'StructDefinition') { |
|
|
|
|
const completion = { |
|
|
|
|
label: { label: `"${node.name}"`, description: await getLinks(node), detail: ` ${node.name}` }, |
|
|
|
|
label: { label: `"${node.name}"`, description: await getNodeLink(node), detail: ` ${node.name}` }, |
|
|
|
|
kind: this.monaco.languages.CompletionItemKind.Struct, |
|
|
|
|
insertText: node.name, |
|
|
|
|
range: range, |
|
|
|
@ -313,7 +227,7 @@ export class RemixCompletionProvider implements languages.CompletionItemProvider |
|
|
|
|
} else if |
|
|
|
|
(node.nodeType === 'EnumDefinition') { |
|
|
|
|
const completion = { |
|
|
|
|
label: { label: `"${node.name}"`, description: await getLinks(node), detail: ` ${node.name}` }, |
|
|
|
|
label: { label: `"${node.name}"`, description: await getNodeLink(node), detail: ` ${node.name}` }, |
|
|
|
|
kind: this.monaco.languages.CompletionItemKind.Enum, |
|
|
|
|
insertText: node.name, |
|
|
|
|
range: range, |
|
|
|
@ -323,7 +237,7 @@ export class RemixCompletionProvider implements languages.CompletionItemProvider |
|
|
|
|
} else if |
|
|
|
|
(node.nodeType === 'EventDefinition') { |
|
|
|
|
const completion = { |
|
|
|
|
label: { label: `"${node.name}"`, description: await getLinks(node), detail: ` ${node.name}` }, |
|
|
|
|
label: { label: `"${node.name}"`, description: await getNodeLink(node), detail: ` ${node.name}` }, |
|
|
|
|
kind: this.monaco.languages.CompletionItemKind.Event, |
|
|
|
|
insertText: node.name, |
|
|
|
|
range: range, |
|
|
|
@ -333,7 +247,7 @@ export class RemixCompletionProvider implements languages.CompletionItemProvider |
|
|
|
|
} else if |
|
|
|
|
(node.nodeType === 'ModifierDefinition') { |
|
|
|
|
const completion = { |
|
|
|
|
label: { label: `"${node.name}"`, description: await getLinks(node), detail: ` ${node.name}` }, |
|
|
|
|
label: { label: `"${node.name}"`, description: await getNodeLink(node), detail: ` ${node.name}` }, |
|
|
|
|
kind: this.monaco.languages.CompletionItemKind.Method, |
|
|
|
|
insertText: node.name, |
|
|
|
|
range: range, |
|
|
|
@ -345,18 +259,6 @@ export class RemixCompletionProvider implements languages.CompletionItemProvider |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
suggestions = [...suggestions, |
|
|
|
|
/* ...GetGlobalVariable(range, this.monaco), |
|
|
|
|
...getCompletionSnippets(range, this.monaco),
|
|
|
|
|
...getBlockCompletionItems(range, this.monaco),
|
|
|
|
|
...GetCompletionTypes(range,this.monaco),
|
|
|
|
|
...GetCompletionKeywords(range,this.monaco), |
|
|
|
|
...GetGlobalFunctions(range,this.monaco), |
|
|
|
|
...GeCompletionUnits(range,this.monaco), |
|
|
|
|
...getMsgCompletionItems(range,this.monaco), |
|
|
|
|
...getTxCompletionItems(range,this.monaco), */ |
|
|
|
|
] |
|
|
|
|
|
|
|
|
|
console.log(suggestions) |
|
|
|
|
return { |
|
|
|
|
suggestions |
|
|
|
@ -368,7 +270,7 @@ export class RemixCompletionProvider implements languages.CompletionItemProvider |
|
|
|
|
if (node.linearizedBaseContracts) { |
|
|
|
|
for (const id of node.linearizedBaseContracts) { |
|
|
|
|
if (id !== node.id) { |
|
|
|
|
const baseContract = await this.props.plugin.call('contextualListener', 'getNodeById', id) |
|
|
|
|
const baseContract = await this.props.plugin.call('codeParser', 'getNodeById', id) |
|
|
|
|
params = [...params, ...[baseContract]] |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
@ -378,37 +280,86 @@ export class RemixCompletionProvider implements languages.CompletionItemProvider |
|
|
|
|
|
|
|
|
|
private getContractCompletions = async (nodes: any[], position: Position) => { |
|
|
|
|
const cursorPosition = this.props.editorAPI.getCursorPosition() |
|
|
|
|
let nodesAtPosition = await this.props.plugin.call('contextualListener', 'nodesAtEditorPosition', cursorPosition) |
|
|
|
|
let nodesAtPosition = await this.props.plugin.call('codeParser', 'nodesAtPosition', cursorPosition) |
|
|
|
|
|
|
|
|
|
// if no nodes exits at position, try to get the block of which the position is in
|
|
|
|
|
if (!nodesAtPosition.length) { |
|
|
|
|
const block = await this.props.plugin.call('contextualListener', 'getBlockName', position, null) |
|
|
|
|
const block = await this.props.plugin.call('codeParser', 'getBlockAtPosition', position, null) |
|
|
|
|
if (block) { |
|
|
|
|
nodesAtPosition = await this.props.plugin.call('contextualListener', 'nodesAtEditorPosition', block.range[0]) |
|
|
|
|
nodesAtPosition = await this.props.plugin.call('codeParser', 'nodesAtPosition', block.range[0]) |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// get all children of all nodes at position
|
|
|
|
|
for (const node of nodesAtPosition) { |
|
|
|
|
const nodesOfScope = await this.props.plugin.call('contextualListener', 'nodesWithScope', node.id) |
|
|
|
|
for (const nodeOfScope of nodesOfScope) { |
|
|
|
|
const imports = await this.props.plugin.call('contextualListener', 'resolveImports', nodeOfScope) |
|
|
|
|
if (imports) { |
|
|
|
|
for (const key in imports) { |
|
|
|
|
if (imports[key].nodes) |
|
|
|
|
nodes = [...nodes, ...imports[key].nodes] |
|
|
|
|
if (isArray(nodesAtPosition)) { |
|
|
|
|
for (const node of nodesAtPosition) { |
|
|
|
|
const nodesOfScope = await this.props.plugin.call('codeParser', 'getNodesWithScope', node.id) |
|
|
|
|
for (const nodeOfScope of nodesOfScope) { |
|
|
|
|
const imports = await this.props.plugin.call('codeParser', 'resolveImports', nodeOfScope) |
|
|
|
|
if (imports) { |
|
|
|
|
for (const key in imports) { |
|
|
|
|
if (imports[key].nodes) |
|
|
|
|
nodes = [...nodes, ...imports[key].nodes] |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
nodes = [...nodes, ...nodesOfScope] |
|
|
|
|
} |
|
|
|
|
nodes = [...nodes, ...nodesOfScope] |
|
|
|
|
} |
|
|
|
|
// get the linearized base contracts
|
|
|
|
|
for (const node of nodesAtPosition) { |
|
|
|
|
const baseContracts = await this.getlinearizedBaseContracts(node) |
|
|
|
|
for (const baseContract of baseContracts) { |
|
|
|
|
nodes = [...nodes, ...baseContract.nodes] |
|
|
|
|
// get the linearized base contracts
|
|
|
|
|
for (const node of nodesAtPosition) { |
|
|
|
|
const baseContracts = await this.getlinearizedBaseContracts(node) |
|
|
|
|
for (const baseContract of baseContracts) { |
|
|
|
|
nodes = [...nodes, ...baseContract.nodes] |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} else { |
|
|
|
|
// get all the nodes from a simple code parser which only parses the current file
|
|
|
|
|
nodes = [...nodes, ...await this.props.plugin.call('codeParser', 'listAstNodes')] |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
return nodes |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
*
|
|
|
|
|
* @param lineTextBeforeCursor
|
|
|
|
|
* @returns
|
|
|
|
|
*/ |
|
|
|
|
private async getLastNodeInExpression(lineTextBeforeCursor: string) { |
|
|
|
|
|
|
|
|
|
const wrapLineInFunction = async(text: string) => { |
|
|
|
|
return `function() {
|
|
|
|
|
${text} |
|
|
|
|
}` |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
let lastNodeInExpression |
|
|
|
|
|
|
|
|
|
const linesToCheck = |
|
|
|
|
[ |
|
|
|
|
lineTextBeforeCursor.substring(0, lineTextBeforeCursor.lastIndexOf('.')) + ".lastnode;", |
|
|
|
|
lineTextBeforeCursor.substring(0, lineTextBeforeCursor.lastIndexOf('.')) + ".lastnode;}", |
|
|
|
|
lineTextBeforeCursor.substring(0, lineTextBeforeCursor.lastIndexOf('.')) + ".lastnode);", |
|
|
|
|
await wrapLineInFunction(lineTextBeforeCursor.substring(0, lineTextBeforeCursor.lastIndexOf('.')) + ".lastnode;"), |
|
|
|
|
await wrapLineInFunction(lineTextBeforeCursor.substring(0, lineTextBeforeCursor.lastIndexOf('.')) + ".lastnode;}"), |
|
|
|
|
await wrapLineInFunction(lineTextBeforeCursor.substring(0, lineTextBeforeCursor.lastIndexOf('.')) + ".lastnode;)"), |
|
|
|
|
await wrapLineInFunction(lineTextBeforeCursor.substring(0, lineTextBeforeCursor.lastIndexOf('.')) + ".lastnode)"), |
|
|
|
|
await wrapLineInFunction(lineTextBeforeCursor.substring(0, lineTextBeforeCursor.lastIndexOf('.')) + ".lastnode);"), |
|
|
|
|
] |
|
|
|
|
|
|
|
|
|
for (const line of linesToCheck) { |
|
|
|
|
try { |
|
|
|
|
const lineAst = await this.props.plugin.call('codeParser', 'parseSolidity', line) |
|
|
|
|
const lastNode = await this.props.plugin.call('codeParser', 'getLastNodeInLine', lineAst) |
|
|
|
|
if(lastNode) { |
|
|
|
|
lastNodeInExpression = lastNode |
|
|
|
|
break |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
} catch (e) { |
|
|
|
|
|
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
return lastNodeInExpression |
|
|
|
|
} |
|
|
|
|
} |