diff --git a/libs/remix-astwalker/src/astWalker.ts b/libs/remix-astwalker/src/astWalker.ts index 4288ec980e..853ff647b5 100644 --- a/libs/remix-astwalker/src/astWalker.ts +++ b/libs/remix-astwalker/src/astWalker.ts @@ -1,6 +1,5 @@ import { EventEmitter } from 'events' import { Node, AstNode } from './index' -import type { CompilationError } from '@remix-project/remix-solidity-ts' export declare interface AstWalker { new(): EventEmitter; 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 ff3e5f5634..8ec73289c5 100644 --- a/libs/remix-core-plugin/src/lib/editor-context-listener.ts +++ b/libs/remix-core-plugin/src/lib/editor-context-listener.ts @@ -7,11 +7,9 @@ import { Compiler } from '@remix-project/remix-solidity' import { helper } from '@remix-project/remix-solidity' import type { CompilationError } from '@remix-project/remix-solidity-ts' - - const profile = { name: 'contextualListener', - methods: ['getBlockName', 'getAST', 'nodesWithScope', 'getNodes', 'compile', 'getNodeById', 'getLastCompilationResult', 'positionOfDefinition', 'definitionAtPosition', 'jumpToDefinition', 'referrencesAtPosition', 'nodesAtEditorPosition', 'referencesOf', 'getActiveHighlights', 'gasEstimation', 'declarationOf', 'jumpToPosition'], + methods: ['getBlockName', 'resolveImports', 'getAST', 'nodesWithScope', 'nodesWithName', 'getNodes', 'compile', 'getNodeById', 'getLastCompilationResult', 'positionOfDefinition', 'definitionAtPosition', 'jumpToDefinition', 'referrencesAtPosition', 'nodesAtEditorPosition', 'referencesOf', 'getActiveHighlights', 'gasEstimation', 'declarationOf', 'jumpToPosition'], events: [], version: '0.0.1' } @@ -43,9 +41,7 @@ export class EditorContextListener extends Plugin { codeDepositCost: any contract: any activated: boolean - lastCompilationResult: any - lastAST: any compiler: any onAstFinished: (success: any, data: any, source: any, input: any, version: any) => Promise @@ -70,16 +66,16 @@ export class EditorContextListener extends Plugin { this._stopHighlighting() }) - this.on('solidity', 'loadingCompiler', async(url) => { + this.on('solidity', 'loadingCompiler', async (url) => { console.log('loading compiler', url) - this.compiler.event.register('compilerLoaded', async() => await this.compile() ) + this.compiler.event.register('compilerLoaded', async () => await this.compile()) 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.onAstFinished = async (success, data, source, input, version) => { console.log('compile success', success) @@ -129,7 +125,7 @@ export class EditorContextListener extends Plugin { //await this.compile() }, 1000) - + setInterval(async () => { const compilationResult = this.lastCompilationResult // await this.call('compilerArtefacts', 'getLastCompilationResult') @@ -173,6 +169,21 @@ export class EditorContextListener extends Plugin { } } + async resolveImports(node, imported = {}) { + if (node.nodeType === 'ImportDirective' && !imported[node.sourceUnit]) { + console.log('IMPORTING', node) + const importNode = await this.getNodeById(node.sourceUnit) + imported[importNode.id] = importNode + if (importNode.nodes) { + for (const child of importNode.nodes) { + imported = await this.resolveImports(child, imported) + } + } + } + console.log(imported) + return imported + } + async getBlockName(position: any, text: string = null) { await this.getAST(text) const allowedTypes = ['SourceUnit', 'ContractDefinition', 'FunctionDefinition'] @@ -191,7 +202,7 @@ export class EditorContextListener extends Plugin { } return null } - if(!this.lastAST) return + if (!this.lastAST) return return walkAst(this.lastAST) } @@ -243,7 +254,7 @@ export class EditorContextListener extends Plugin { return results } - async nodesAtEditorPosition(position: any, type: string = '') { + async nodesAtEditorPosition(position: any, type = '') { const lastCompilationResult = this.lastCompilationResult // await this.call('compilerArtefacts', 'getLastCompilationResult') if (!lastCompilationResult) return false const urlFromPath = await this.call('fileManager', 'getUrlFromPath', this.currentFile) @@ -280,6 +291,14 @@ export class EditorContextListener extends Plugin { return nodes } + async nodesWithName(name: string) { + const nodes = [] + for (const node of Object.values(this._index.FlatReferences) as any[]) { + if (node.name === name) nodes.push(node) + } + return nodes + } + async definitionAtPosition(position: any) { const nodes = await this.nodesAtEditorPosition(position) console.log('nodes at position', nodes) diff --git a/libs/remix-core-plugin/tsconfig.json b/libs/remix-core-plugin/tsconfig.json index c23e61c800..7f163468b2 100644 --- a/libs/remix-core-plugin/tsconfig.json +++ b/libs/remix-core-plugin/tsconfig.json @@ -1,10 +1,8 @@ { "extends": "../../tsconfig.base.json", - "files": [], - "include": [], - "references": [ - { - "path": "./tsconfig.lib.json" - } - ] -} + "compilerOptions": { + "types": ["node"], + "esModuleInterop": true + }, + "include": ["**/*.ts"] +} \ No newline at end of file diff --git a/libs/remix-core-plugin/tsconfig.lib.json b/libs/remix-core-plugin/tsconfig.lib.json index 7e93feb2f8..4c89a574be 100644 --- a/libs/remix-core-plugin/tsconfig.lib.json +++ b/libs/remix-core-plugin/tsconfig.lib.json @@ -3,10 +3,15 @@ "compilerOptions": { "module": "commonjs", "outDir": "../../dist/out-tsc", + "allowSyntheticDefaultImports": true, + "esModuleInterop": true, "declaration": true, "rootDir": "./src", "types": ["node"] }, - "exclude": ["**/*.spec.ts"], + "exclude": [ + "**/*.spec.ts", + "tests/" + ], "include": ["**/*.ts"] } diff --git a/libs/remix-solidity/package.json b/libs/remix-solidity/package.json index 351f9ab46f..d9f8c5f2e5 100644 --- a/libs/remix-solidity/package.json +++ b/libs/remix-solidity/package.json @@ -1,5 +1,5 @@ { - "name": "@remix-project/remix-solidity", + "name": "@remix-project/remix-solidity-ts", "version": "0.4.13", "description": "Tool to load and run Solidity compiler", "main": "src/index.js", diff --git a/libs/remix-tests/src/compiler.ts b/libs/remix-tests/src/compiler.ts index f16c925edc..6e0d9696da 100644 --- a/libs/remix-tests/src/compiler.ts +++ b/libs/remix-tests/src/compiler.ts @@ -3,7 +3,7 @@ import async from 'async' import path from 'path' import deepequal from 'deep-equal' import Log from './logger' -import { Compiler as RemixCompiler } from '@remix-project/remix-solidity' +import { Compiler } from '@remix-project/remix-solidity' import { SrcIfc, CompilerConfiguration, CompilationErrors } from './types' const logger = new Log() const log = logger.logger @@ -114,7 +114,7 @@ export function compileFileOrFiles (filename: string, isDirectory: boolean, opts } finally { async.waterfall([ function loadCompiler (next) { - compiler = new RemixCompiler((url, cb) => { + compiler = new Compiler((url, cb) => { try { cb(null, fs.readFileSync(url, 'utf-8')) } catch (e) { @@ -186,7 +186,7 @@ export function compileContractSources (sources: SrcIfc, newCompConfig: any, imp if (!compiler || !deepequal(UTRunner.compilerConfig, newCompConfig)) { UTRunner.compilerConfig = newCompConfig const { currentCompilerUrl, evmVersion, optimize, runs, usingWorker } = newCompConfig - compiler = new RemixCompiler(importFileCb) + compiler = new Compiler(importFileCb) compiler.set('evmVersion', evmVersion) compiler.set('optimize', optimize) compiler.set('runs', runs) diff --git a/libs/remix-tests/tsconfig.json b/libs/remix-tests/tsconfig.json index 3b54496f3d..e960ad42b3 100644 --- a/libs/remix-tests/tsconfig.json +++ b/libs/remix-tests/tsconfig.json @@ -4,8 +4,7 @@ "types": ["node", "jest"], "module": "commonjs", "esModuleInterop": true, - "allowJs": true, - "rootDir": "./", + "rootDir": "./src", }, "include": ["**/*.ts"] } \ No newline at end of file diff --git a/libs/remix-ui/editor/src/lib/providers/completionProvider.ts b/libs/remix-ui/editor/src/lib/providers/completionProvider.ts index 4c2f40aacd..d3863f7f95 100644 --- a/libs/remix-ui/editor/src/lib/providers/completionProvider.ts +++ b/libs/remix-ui/editor/src/lib/providers/completionProvider.ts @@ -1,6 +1,10 @@ -export class RemixCompletionProvider { +import { editor, languages, Position } from "monaco-editor" +import monaco from "../../types/monaco" +import { EditorUIProps } from "../remix-ui-editor" - props: any +export class RemixCompletionProvider implements languages.CompletionItemProvider { + + props: EditorUIProps monaco: any constructor(props: any, monaco: any) { this.props = props @@ -8,31 +12,10 @@ export class RemixCompletionProvider { } triggerCharacters = ['.', ''] - async provideCompletionItems(model: any, position: any, context: any) { + async provideCompletionItems(model: editor.ITextModel, position: Position, context: monaco.languages.CompletionContext) { console.log('AUTOCOMPLETE', context) console.log(position) - //await this.props.plugin.call('contextualListener', 'compile') - //const block = await this.props.plugin.call('contextualListener', 'getBlockName', position) - //console.log('BLOCK', block) - //return null - return await this.run(model, position, context) - - const word = model.getWordUntilPosition(position); - const wordAt = model.getWordAtPosition(position); - console.log('WORD', word) - console.log('WORDAT', wordAt) - - return new Promise((resolve, reject) => { - this.props.plugin.once('contextualListener', 'astFinished', async () => { - console.log('AST FINISHED') - resolve(await this.run(model, position, context)) - }) - this.props.plugin.call('contextualListener', 'compile') - }) - } - - async run(model: any, position: any, context: any) { const textUntilPosition = model.getValueInRange({ startLineNumber: 1, startColumn: 1, @@ -68,7 +51,7 @@ export class RemixCompletionProvider { const line = model.getLineContent(position.lineNumber) - let nodes + let nodes = [] @@ -119,14 +102,15 @@ export class RemixCompletionProvider { let nodesAtPosition = await this.props.plugin.call('contextualListener', 'nodesAtEditorPosition', cursorPosition) console.log('NODES AT POSITION', nodesAtPosition) - 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) - } + const block = await this.props.plugin.call('contextualListener', 'getBlockName', position, textWithoutEdits) + 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]) + console.log('NODES AT POSITION WITH BLOCK', nodesAtPosition) } + //} + // explore nodes at the BLOCK if (nodesAtPosition) { for (const node of nodesAtPosition) { const nodesOfScope = await this.props.plugin.call('contextualListener', 'nodesWithScope', node.id) @@ -137,7 +121,7 @@ export class RemixCompletionProvider { 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 || declarationOf.members + nodes = [...nodes, ...declarationOf.nodes || declarationOf.members] const baseContracts = await getlinearizedBaseContracts(declarationOf) for (const baseContract of baseContracts) { nodes = [...nodes, ...baseContract.nodes] @@ -146,7 +130,46 @@ export class RemixCompletionProvider { } } } + // 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('contextualListener', 'declarationOf', statement.expression) + if (declarationOf.typeName && declarationOf.typeName.nodeType === 'UserDefinedTypeName') { + const baseDeclaration = await this.props.plugin.call('contextualListener', 'declarationOf', declarationOf.typeName) + console.log('HAS BASE DECLARATION OF', baseDeclaration) + nodes = [...nodes, ...baseDeclaration.nodes || baseDeclaration.members] + } + } + } + } + } + } } + + + // brute force search in all nodes with the name + //if (!nodes.length) { + const nodesOfScope = await this.props.plugin.call('contextualListener', 'nodesWithName', 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) + console.log('HAS DECLARATION OF', declarationOf) + nodes = [...nodes,...declarationOf.nodes || declarationOf.members] + const baseContracts = await getlinearizedBaseContracts(declarationOf) + for (const baseContract of baseContracts) { + nodes = [...nodes, ...baseContract.nodes] + } + } + } + } + + //} } } else { const cursorPosition = this.props.editorAPI.getCursorPosition() @@ -162,16 +185,32 @@ export class RemixCompletionProvider { } } + // 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] + } + } + } + + nodes = [...nodes, ...nodesOfScope] } + // get the linearized base contracts for (const node of nodesAtPosition) { const baseContracts = await getlinearizedBaseContracts(node) for (const baseContract of baseContracts) { nodes = [...nodes, ...baseContract.nodes] } } + + + //nodes = await this.props.plugin.call('contextualListener', 'getNodes') } diff --git a/libs/remix-ui/editor/src/lib/providers/hoverProvider.ts b/libs/remix-ui/editor/src/lib/providers/hoverProvider.ts index 548c1554d0..61b320d66e 100644 --- a/libs/remix-ui/editor/src/lib/providers/hoverProvider.ts +++ b/libs/remix-ui/editor/src/lib/providers/hoverProvider.ts @@ -1,27 +1,18 @@ +import { editor, languages, Position } from 'monaco-editor' +import monaco from '../../types/monaco' +import { EditorUIProps } from '../remix-ui-editor' +export class RemixHoverProvider implements languages.HoverProvider { -export class RemixHoverProvider { - - props: any + props: EditorUIProps monaco: any constructor(props: any, monaco: any) { this.props = props this.monaco = monaco } - provideHover = async function (model: any, position: any) { + provideHover = async function (model: editor.ITextModel, position: Position) { console.log('HOVERING') - return await this.run(model, position) - return new Promise((resolve, reject) => { - this.props.plugin.once('contextualListener', 'astFinished', async () => { - console.log('AST FINISHED') - resolve(await this.run(model, position)) - }) - this.props.plugin.call('contextualListener', 'compile') - }) - - } - async run(model: any, position: any) { const cursorPosition = this.props.editorAPI.getHoverPosition(position) const nodeAtPosition = await this.props.plugin.call('contextualListener', 'definitionAtPosition', cursorPosition) diff --git a/libs/remix-ui/editor/src/lib/remix-ui-editor.tsx b/libs/remix-ui/editor/src/lib/remix-ui-editor.tsx index 614daf14ff..1de147871d 100644 --- a/libs/remix-ui/editor/src/lib/remix-ui-editor.tsx +++ b/libs/remix-ui/editor/src/lib/remix-ui-editor.tsx @@ -8,8 +8,8 @@ import { cairoLang, cairoConf } from './cairoSyntax' import './remix-ui-editor.css' import { loadTypes } from './web-types' import monaco from '../types/monaco' -import { IPosition, languages } from 'monaco-editor' -import { sourceMappingDecoder } from '@remix-project/remix-debug' +import { IPosition } from 'monaco-editor' + import { RemixHoverProvider } from './providers/hoverProvider' import { RemixReferenceProvider } from './providers/referenceProvider' import { RemixCompletionProvider } from './providers/completionProvider' @@ -103,7 +103,7 @@ export const EditorUI = (props: EditorUIProps) => { \t\t\t\t\t\t\t\tTwitter: https://twitter.com/ethereumremix\n ` const editorRef = useRef(null) - const monacoRef = useRef(null) + const monacoRef = useRef(null) const currentFileRef = useRef('') // const currentDecorations = useRef({ sourceAnnotationsPerFile: {}, markerPerFile: {} }) // decorations that are currently in use by the editor // const registeredDecorations = useRef({}) // registered decorations @@ -428,31 +428,34 @@ export const EditorUI = (props: EditorUIProps) => { (window as any).addRemixBreakpoint(e.target.position) } }) - editor.addCommand(monacoRef.current.KeyMod.CtrlCmd | monacoRef.current.KeyCode.US_EQUAL, () => { + editor.addCommand(monacoRef.current.KeyMod.CtrlCmd | (monacoRef.current.KeyCode as any).US_EQUAL, () => { editor.updateOptions({ fontSize: editor.getOption(43).fontSize + 1 }) }) - editor.addCommand(monacoRef.current.KeyMod.CtrlCmd | monacoRef.current.KeyCode.US_MINUS, () => { + editor.addCommand(monacoRef.current.KeyMod.CtrlCmd | (monacoRef.current.KeyCode as any).US_MINUS, () => { editor.updateOptions({ fontSize: editor.getOption(43).fontSize - 1 }) }) } - function handleEditorWillMount(monaco) { + function handleEditorWillMount(monaco: Monaco) { + console.log('editor will mount', monaco, typeof monaco) monacoRef.current = monaco // Register a new language monacoRef.current.languages.register({ id: 'remix-solidity' }) monacoRef.current.languages.register({ id: 'remix-cairo' }) // Register a tokens provider for the language - monacoRef.current.languages.setMonarchTokensProvider('remix-solidity', language) - monacoRef.current.languages.setLanguageConfiguration('remix-solidity', conf) + monacoRef.current.languages.setMonarchTokensProvider('remix-solidity', language as any) + monacoRef.current.languages.setLanguageConfiguration('remix-solidity', conf as any) - monacoRef.current.languages.setMonarchTokensProvider('remix-cairo', cairoLang) - monacoRef.current.languages.setLanguageConfiguration('remix-cairo', cairoConf) + monacoRef.current.languages.setMonarchTokensProvider('remix-cairo', cairoLang as any) + monacoRef.current.languages.setLanguageConfiguration('remix-cairo', cairoConf as any) // register Definition Provider monacoRef.current.languages.registerDefinitionProvider('remix-solidity', { - provideDefinition(model: monaco.editor.ITextModel, position: monaco.Position, token: monaco.CancellationToken) { + + provideDefinition(model:any, position: any, token: any) { const cursorPosition = props.editorAPI.getCursorPosition() props.plugin.call('contextualListener', 'jumpToDefinition', cursorPosition) + return null } }) diff --git a/package.json b/package.json index ec1788e7c2..94251acdfa 100644 --- a/package.json +++ b/package.json @@ -323,7 +323,7 @@ "ts-jest": "^27.0.5", "ts-node": "^7.0.1", "tslint": "~6.0.0", - "typescript": "^4.4.3", + "typescript": "^4.7.2", "uglify-js": "^2.8.16", "vm-browserify": "0.0.4", "watchify": "^3.9.0", diff --git a/yarn.lock b/yarn.lock index 6139c5e6c5..de7d5b338b 100644 --- a/yarn.lock +++ b/yarn.lock @@ -22536,10 +22536,10 @@ typedarray@^0.0.6: resolved "https://registry.yarnpkg.com/typedarray/-/typedarray-0.0.6.tgz#867ac74e3864187b1d3d47d996a78ec5c8830777" integrity sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c= -typescript@^4.4.3: - version "4.4.4" - resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.4.4.tgz#2cd01a1a1f160704d3101fd5a58ff0f9fcb8030c" - integrity sha512-DqGhF5IKoBl8WNf8C1gu8q0xZSInh9j1kJJMqT3a94w1JzVaBU4EXOSMrz9yDqMT0xt3selp83fuFMQ0uzv6qA== +typescript@^4.7.2: + version "4.7.2" + resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.7.2.tgz#1f9aa2ceb9af87cca227813b4310fff0b51593c4" + integrity sha512-Mamb1iX2FDUpcTRzltPxgWMKy3fhg0TN378ylbktPGPK/99KbDtMQ4W1hwgsbPAsG3a0xKa1vmw4VKZQbkvz5A== uglify-js@^2.8.16: version "2.8.29"