From 9914fc126930f414fcfe7a5f0cd3ada2a78c6ea5 Mon Sep 17 00:00:00 2001 From: filip mertens Date: Sat, 28 May 2022 18:29:36 +0200 Subject: [PATCH] use blocks --- .../solidity-compiler/src/app/compiler-api.ts | 8 +- .../src/lib/editor-context-listener.ts | 43 ++++--- .../src/compiler/compiler-input.ts | 4 +- .../src/lib/remix-ui-editor-context-view.tsx | 2 +- .../src/lib/providers/completionProvider.ts | 107 ++++++++++++------ .../editor/src/lib/providers/hoverProvider.ts | 2 +- 6 files changed, 107 insertions(+), 59 deletions(-) diff --git a/apps/solidity-compiler/src/app/compiler-api.ts b/apps/solidity-compiler/src/app/compiler-api.ts index 75c49ef26f..32a4caf361 100644 --- a/apps/solidity-compiler/src/app/compiler-api.ts +++ b/apps/solidity-compiler/src/app/compiler-api.ts @@ -222,6 +222,7 @@ export const CompilerApiMixin = (Base) => class extends Base { this.data.loading = true this.data.loadingUrl = url this.statusChanged({ key: 'loading', title: 'loading compiler...', type: 'info' }) + this.emit('loadingCompiler', url) } this.compiler.event.register('loadingCompiler', this.data.eventHandlers.onLoadingCompiler) @@ -321,13 +322,10 @@ export const CompilerApiMixin = (Base) => class extends Base { } } - this.data.eventHandlers.onAstFinished = async (success, data, source, input, version) => { - this.emit('astFinished', source.target, source, 'soljson', data, input, version) - } - this.compiler.event.register('compilationFinished', this.data.eventHandlers.onCompilationFinished) - this.compiler.event.register('astFinished', this.data.eventHandlers.onAstFinished) + this.compiler.event.register('compilationFinished', this.data.eventHandlers.onCompilationFinished) + this.data.eventHandlers.onThemeChanged = (theme) => { const invert = theme.quality === 'dark' ? 1 : 0 const img = document.getElementById('swarmLogo') diff --git a/libs/remix-core-plugin/src/lib/editor-context-listener.ts b/libs/remix-core-plugin/src/lib/editor-context-listener.ts index 5c6e85a513..eb139a8e11 100644 --- a/libs/remix-core-plugin/src/lib/editor-context-listener.ts +++ b/libs/remix-core-plugin/src/lib/editor-context-listener.ts @@ -43,6 +43,7 @@ export class EditorContextListener extends Plugin { lastAST: any compiler: any + onAstFinished: (success: any, data: any, source: any, input: any, version: any) => Promise constructor(astWalker) { super(profile) @@ -56,20 +57,26 @@ export class EditorContextListener extends Plugin { this.astWalker = astWalker } - onActivation() { + async onActivation() { this.on('editor', 'contentChanged', async () => { await this.getAST() this._stopHighlighting() }) + this.on('solidity', 'loadingCompiler', async(url) => { + console.log('loading compiler', url) + this.compiler.loadVersion(true, url) + }) + this.compiler = new Compiler((url, cb) => this.call('contentImport', 'resolveAndSave', url, undefined, false).then((result) => cb(null, result)).catch((error) => cb(error.message))) - this.compiler.loadVersion(true, 'https://binaries.soliditylang.org/wasm/soljson-v0.8.7+commit.e28d00a7.js') + + + + this.onAstFinished = async (success, data, source, input, version) => { - this.on('solidity', 'astFinished', async (file, source, languageVersion, data, input, version) => { - // console.log('compilation result', Object.keys(data.sources)) - if (languageVersion.indexOf('soljson') !== 0 || !data.sources) return + if (!data.sources) return if (data.sources && Object.keys(data.sources).length === 0) return - this.lastCompilationResult = new CompilerAbstract(languageVersion, data, source, input) + this.lastCompilationResult = new CompilerAbstract('soljson', data, source, input) this._stopHighlighting() this._index = { @@ -78,13 +85,15 @@ export class EditorContextListener extends Plugin { } this._buildIndex(data, source) this.emit('astFinished') - }) + } + + this.compiler.event.register('astFinished', this.onAstFinished) setInterval(async () => { + await this.compile() }, 5000) - setInterval(async () => { const compilationResult = this.lastCompilationResult // await this.call('compilerArtefacts', 'getLastCompilationResult') if (compilationResult && compilationResult.languageversion.indexOf('soljson') === 0) { @@ -110,6 +119,12 @@ export class EditorContextListener extends Plugin { async compile() { try { + const state = await this.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.currentFile = await this.call('fileManager', 'file') if (!this.currentFile) return const content = await this.call('fileManager', 'readFile', this.currentFile) @@ -119,8 +134,8 @@ export class EditorContextListener extends Plugin { } } - async getBlockName(position: any) { - await this.getAST() + async getBlockName(position: any, text: string = null) { + await this.getAST(text) const allowedTypes = ['SourceUnit', 'ContractDefinition', 'FunctionDefinition'] const walkAst = (node) => { @@ -137,19 +152,19 @@ export class EditorContextListener extends Plugin { } return null } - + if(!this.lastAST) return return walkAst(this.lastAST) } - async getAST() { + async getAST(text: string = null) { this.currentFile = await this.call('fileManager', 'file') if (!this.currentFile) return - let fileContent = await this.call('fileManager', 'readFile', this.currentFile) + let fileContent = text || await this.call('fileManager', 'readFile', this.currentFile) try { const ast = (SolidityParser as any).parse(fileContent, { loc: true, range: true, tolerant: true }) this.lastAST = ast } catch (e) { - + console.log(e) } console.log('LAST AST', this.lastAST) return this.lastAST diff --git a/libs/remix-solidity/src/compiler/compiler-input.ts b/libs/remix-solidity/src/compiler/compiler-input.ts index 0b214b5201..ae22dc33de 100644 --- a/libs/remix-solidity/src/compiler/compiler-input.ts +++ b/libs/remix-solidity/src/compiler/compiler-input.ts @@ -16,9 +16,9 @@ export default (sources: Source, opts: CompilerInputOptions): string => { outputSelection: { '*': { '': ['ast'], - //'*': [] + '*': [] //'*' : ['abi', 'metadata'] - '*': ['abi', 'metadata', 'devdoc', 'userdoc', 'storageLayout', 'evm.legacyAssembly', 'evm.bytecode', 'evm.deployedBytecode', 'evm.methodIdentifiers', 'evm.gasEstimates', 'evm.assembly'] + //'*': ['abi', 'metadata', 'devdoc', 'userdoc', 'storageLayout', 'evm.legacyAssembly', 'evm.bytecode', 'evm.deployedBytecode', 'evm.methodIdentifiers', 'evm.gasEstimates', 'evm.assembly'] } } } diff --git a/libs/remix-ui/editor-context-view/src/lib/remix-ui-editor-context-view.tsx b/libs/remix-ui/editor-context-view/src/lib/remix-ui-editor-context-view.tsx index 2946b41307..8e9a952e23 100644 --- a/libs/remix-ui/editor-context-view/src/lib/remix-ui-editor-context-view.tsx +++ b/libs/remix-ui/editor-context-view/src/lib/remix-ui-editor-context-view.tsx @@ -115,7 +115,7 @@ export function RemixUiEditorContextView (props: RemixUiEditorContextViewProps) * show gas estimation */ const gasEstimation = (node) => { - if (node.nodeType === 'FunctionDefinition') { + if (node.nodeType === 'FunctionDefinition' && state && state.gasEstimation) { const result: gasEstimationType = state.gasEstimation const executionCost = ' Execution cost: ' + result.executionCost + ' gas' const codeDepositCost = 'Code deposit cost: ' + result.codeDepositCost + ' gas' diff --git a/libs/remix-ui/editor/src/lib/providers/completionProvider.ts b/libs/remix-ui/editor/src/lib/providers/completionProvider.ts index bcf326f4e5..8a304ea547 100644 --- a/libs/remix-ui/editor/src/lib/providers/completionProvider.ts +++ b/libs/remix-ui/editor/src/lib/providers/completionProvider.ts @@ -40,6 +40,9 @@ export class RemixCompletionProvider { endColumn: position.column }); + + + const word = model.getWordUntilPosition(position); const wordAt = model.getWordAtPosition(position); const range = { @@ -69,58 +72,90 @@ export class RemixCompletionProvider { + + const cursorPosition = this.props.editorAPI.getCursorPosition() + console.log('cursor', cursorPosition) + if (context.triggerCharacter === '.') { console.log('TEXT', line) - const splits = line.split('.') + + const textBeforeCursor = line.substring(0, position.column - 1) + const textAfterCursor = line.substring(position.column - 1) + console.log(textBeforeCursor, textAfterCursor) + const splits = textBeforeCursor.split('.') + console.log('splits', splits) if (splits.length > 1) { - const last = splits[splits.length - 2].trim() + let last = splits[splits.length - 2].trim() + const lastParentheses = last.lastIndexOf('(') + const lastBracket = last.lastIndexOf('{') + const lastSemiColon = last.lastIndexOf(';') + let textBefore = null + let lastWord = null + let lineWithoutEdits = null + // find largest + const lastIndex = Math.max(lastParentheses, lastBracket, lastSemiColon) + if (lastIndex > -1) { + lastWord = last.substring(lastIndex + 1) + textBefore = last.substring(0, lastIndex + 1) + console.log('textBefore', textBefore) + console.log('text without edits', textBefore, textAfterCursor) + lineWithoutEdits = `${textBefore}${textAfterCursor}` + } + last = lastWord || last console.log('last', last) - const cursorPosition = this.props.editorAPI.getCursorPosition() - const nodesAtPosition = await this.props.plugin.call('contextualListener', 'nodesAtEditorPosition', cursorPosition) + + const lines = model.getLinesContent() + lines[position.lineNumber - 1] = lineWithoutEdits + + const textWithoutEdits = lines.join('\n') + console.log('textWithoutEdits', textWithoutEdits) + + let nodesAtPosition = await this.props.plugin.call('contextualListener', 'nodesAtEditorPosition', cursorPosition) + console.log('NODES AT POSITION', nodesAtPosition) - for (const node of nodesAtPosition) { - const nodesOfScope = await this.props.plugin.call('contextualListener', 'nodesWithScope', 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) - console.log('HAS DECLARATION OF', declarationOf) - nodes = declarationOf.nodes - } - } + if (!nodesAtPosition.length) { + const block = await this.props.plugin.call('contextualListener', 'getBlockName', position, textWithoutEdits) + console.log('BLOCK', block) + if (block) { + nodesAtPosition = await this.props.plugin.call('contextualListener', 'nodesAtEditorPosition', block.range[0]) + console.log('NODES AT POSITION', nodesAtPosition) } } - } - /* - console.log('TEXT', line) - const splits = line.split('.') - console.log('splits', splits) - if(splits.length > 1) { - const last = splits[splits.length - 2].trim() - console.log('last', last) - for(const node of Object.values(nodes) as any[]){ - if(node.name === last) { - console.log('FOUND', node) - const cursorPosition = this.props.editorAPI.getCursorPosition() - const nodeAtPosition = await this.props.plugin.call('contextualListener', 'definitionAtPosition', cursorPosition) - console.log('NODE AT POSITION', nodeAtPosition) - if(nodeAtPosition && nodeAtPosition.nodeType === 'Block'){ - if(node.scope && node.scope === nodeAtPosition.id) { - console.log('NODE IN SCOPE', node) + if (nodesAtPosition) { + for (const node of nodesAtPosition) { + const nodesOfScope = await this.props.plugin.call('contextualListener', 'nodesWithScope', 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) + console.log('HAS DECLARATION OF', declarationOf) + nodes = declarationOf.nodes + const baseContracts = await getlinearizedBaseContracts(declarationOf) + for (const baseContract of baseContracts) { + nodes = [...nodes, ...baseContract.nodes] + } + } } } } } } - */ } else { const cursorPosition = this.props.editorAPI.getCursorPosition() - const nodesAtPosition = await this.props.plugin.call('contextualListener', 'nodesAtEditorPosition', cursorPosition) + let nodesAtPosition = await this.props.plugin.call('contextualListener', 'nodesAtEditorPosition', cursorPosition) nodes = [] console.log('NODES AT POSITION', nodesAtPosition) + if (!nodesAtPosition.length) { + const block = await this.props.plugin.call('contextualListener', 'getBlockName', position, null) + console.log('BLOCK', block) + if (block) { + nodesAtPosition = await this.props.plugin.call('contextualListener', 'nodesAtEditorPosition', block.range[0]) + console.log('NODES AT POSITION', nodesAtPosition) + } + } for (const node of nodesAtPosition) { const nodesOfScope = await this.props.plugin.call('contextualListener', 'nodesWithScope', node.id) @@ -198,7 +233,7 @@ export class RemixCompletionProvider { const suggestions = [] for (const node of Object.values(nodes) as any[]) { - if(!node.name) continue + if (!node.name) continue if (node.nodeType === 'VariableDeclaration') { const completion = { label: { label: `"${node.name}"`, description: await getLinks(node), detail: ` ${await getVariableDeclaration(node)}` }, diff --git a/libs/remix-ui/editor/src/lib/providers/hoverProvider.ts b/libs/remix-ui/editor/src/lib/providers/hoverProvider.ts index f411efd83b..548c1554d0 100644 --- a/libs/remix-ui/editor/src/lib/providers/hoverProvider.ts +++ b/libs/remix-ui/editor/src/lib/providers/hoverProvider.ts @@ -119,7 +119,7 @@ export class RemixHoverProvider { if (!nodeAtPosition) { contents.push({ - value: 'No definition found. Please compile the source code.' + value: 'Loading...' }) }