more completions

editorcontextDummy
filip mertens 3 years ago
parent a9c762152c
commit 32c1be281e
  1. 15
      apps/remix-ide/src/app/plugins/parser/code-parser.tsx
  2. 1
      apps/remix-ide/src/app/plugins/parser/services/code-parser-compiler.ts
  3. 162
      libs/remix-ui/editor/src/lib/providers/completion/completionGlobals.ts
  4. 45
      libs/remix-ui/editor/src/lib/providers/completionProvider.ts

@ -2,8 +2,6 @@
import { Plugin } from '@remixproject/engine'
import { sourceMappingDecoder } from '@remix-project/remix-debug'
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 CodeParserGasService from './services/code-parser-gas-service'
@ -11,8 +9,6 @@ import CodeParserCompiler from './services/code-parser-compiler'
import CodeParserAntlrService from './services/code-parser-antlr-service'
import CodeParserNodeHelper from './services/code-parser-node-helper'
import React from 'react'
import { fileDecoration, fileDecorationType } from '@remix-ui/file-decorators'
import { Profile } from '@remixproject/plugin-utils'
import { ContractDefinitionAstNode, EventDefinitionAstNode, FunctionCallAstNode, FunctionDefinitionAstNode, IdentifierAstNode, ImportDirectiveAstNode, ModifierDefinitionAstNode, SourceUnitAstNode, StructDefinitionAstNode, VariableDeclarationAstNode } from 'dist/libs/remix-analyzer/src/types'
import { lastCompilationResult, RemixApi } from '@remixproject/plugin-api'
@ -203,7 +199,9 @@ export class CodeParser extends Plugin {
const index = {}
const contractName: string = contractNode.name
const callback = (node) => {
if(inScope && node.scope !== contractNode.id) return
if(inScope && node.scope !== contractNode.id
&& !(node.nodeType === 'EnumDefinition' || node.nodeType === 'EventDefinition' || node.nodeType === 'ModifierDefinition'))
return
if(inScope) node.isClassNode = true;
node.gasEstimate = this._getContractGasEstimate(node, contractName, fileName, compilatioResult)
node.functionName = node.name + this._getInputParams(node)
@ -308,13 +306,6 @@ export class CodeParser extends Plugin {
}
}
/**
* Nodes at position where position is a number, offset
* @param position

@ -95,7 +95,6 @@ export default class CodeParserCompiler {
this.plugin.currentFile = await this.plugin.call('fileManager', 'file')
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)

@ -1,6 +1,33 @@
import { IRange } from "monaco-editor";
import monaco from "../../../types/monaco";
export function getStringCompletionItems(range: IRange, monaco): monaco.languages.CompletionItem[] {
return [
{
detail: 'concatenate an arbitrary number of string values',
kind: monaco.languages.CompletionItemKind.Property,
insertText: 'concat(${1:string})',
insertTextRules: monaco.languages.CompletionItemInsertTextRule.InsertAsSnippet,
label: 'concat()',
range,
},
]
}
export function getBytesCompletionItems(range: IRange, monaco): monaco.languages.CompletionItem[] {
return [
{
detail: 'concatenate an arbitrary number of values',
kind: monaco.languages.CompletionItemKind.Property,
insertText: 'concat(${1:bytes})',
insertTextRules: monaco.languages.CompletionItemInsertTextRule.InsertAsSnippet,
label: 'concat()',
range,
},
]
}
export function getBlockCompletionItems(range: IRange, monaco): monaco.languages.CompletionItem[] {
return [
{
@ -10,6 +37,20 @@ export function getBlockCompletionItems(range: IRange, monaco): monaco.languages
label: 'coinbase',
range,
},
{
detail: '(uint): Current block’s base fee',
kind: monaco.languages.CompletionItemKind.Property,
insertText: 'basefee',
label: 'basefee',
range,
},
{
detail: '(uint): Current chain id',
kind: monaco.languages.CompletionItemKind.Property,
insertText: 'chainid',
label: 'chainid',
range,
},
{
detail: '(bytes32): DEPRICATED In 0.4.22 use blockhash(uint) instead. Hash of the given block - only works for 256 most recent blocks excluding current',
insertText: 'blockhash(${1:blockNumber});',
@ -200,6 +241,14 @@ export function getAbiCompletionItems(range: IRange, monaco): monaco.languages.C
label: 'encode',
range
},
{
detail: 'encodeCall(function functionPointer, (...)) returns (bytes memory) ABI-encodes a call to functionPointer with the arguments found in the tuple',
insertText: 'encode(${1:arg});',
kind: monaco.languages.CompletionItemKind.Method,
insertTextRules: monaco.languages.CompletionItemInsertTextRule.InsertAsSnippet,
label: 'encodecall',
range
},
{
detail: 'encodePacked(..) returns (bytes): Performes packed encoding of the given arguments',
insertText: 'encodePacked(${1:arg});',
@ -260,7 +309,10 @@ function CreateCompletionItem(label: string, kind: monaco.languages.CompletionIt
export function GetCompletionKeywords(range: IRange, monaco): monaco.languages.CompletionItem[] {
const completionItems = [];
const keywords = ['modifier', 'mapping', 'break', 'continue', 'delete', 'else', 'for',
'if', 'new', 'return', 'returns', 'while', 'using', 'emit',
'after', 'promise', 'alias', 'apply','auto', 'copyof', 'default', 'define', 'final', 'implements',
'inline', 'let', 'macro', 'match', 'mutable', 'null', 'of', 'partial', 'reference', 'relocatable',
'sealed', 'sizeof', 'static', 'supports', 'switch', 'typedef',
'if', 'new', 'return', 'returns', 'while', 'using', 'emit', 'anonymous', 'indexed',
'private', 'public', 'external', 'internal', 'payable', 'nonpayable', 'view', 'pure', 'case', 'do', 'else', 'finally',
'in', 'instanceof', 'return', 'throw', 'try', 'catch', 'typeof', 'yield', 'void', 'virtual', 'override'];
keywords.forEach(unit => {
@ -277,6 +329,7 @@ export function GetCompletionKeywords(range: IRange, monaco): monaco.languages.C
completionItems.push(CreateCompletionItem('contract', monaco.languages.CompletionItemKind.Class, null, range));
completionItems.push(CreateCompletionItem('library', monaco.languages.CompletionItemKind.Class, null, range));
completionItems.push(CreateCompletionItem('storage', monaco.languages.CompletionItemKind.Field, null, range));
completionItems.push(CreateCompletionItem('calldata', monaco.languages.CompletionItemKind.Field, null, range));
completionItems.push(CreateCompletionItem('memory', monaco.languages.CompletionItemKind.Field, null, range));
completionItems.push(CreateCompletionItem('var', monaco.languages.CompletionItemKind.Field, null, range));
completionItems.push(CreateCompletionItem('constant', monaco.languages.CompletionItemKind.Constant, null, range));
@ -491,14 +544,119 @@ export function getContextualAutoCompleteByGlobalVariable(word: string, range: I
if (word === 'block') {
return getBlockCompletionItems(range, monaco);
}
if (word === 'string') {
return getStringCompletionItems(range, monaco);
}
if (word === 'bytes') {
return getBytesCompletionItems(range, monaco);
}
if (word === 'msg') {
return getMsgCompletionItems(range, monaco);
}
if (word === 'tx') {
return getTxCompletionItems(range, monaco);
}
if (word === 'address') {
if (word === 'abi') {
return getAbiCompletionItems(range, monaco);
}
if (word === 'sender') {
return getAddressCompletionItems(range, monaco);
}
return null;
}
export function getArrayCompletionItems(range: IRange, monaco): monaco.languages.CompletionItem[] {
return [
{
detail: '',
kind: monaco.languages.CompletionItemKind.Method,
insertText: 'length;',
insertTextRules: monaco.languages.CompletionItemInsertTextRule.InsertAsSnippet,
label: 'length',
range,
},
{
detail: '',
kind: monaco.languages.CompletionItemKind.Method,
insertText: 'push(${1:value});',
insertTextRules: monaco.languages.CompletionItemInsertTextRule.InsertAsSnippet,
label: 'push(value)',
range,
},
{
detail: '',
kind: monaco.languages.CompletionItemKind.Method,
insertText: 'push();',
insertTextRules: monaco.languages.CompletionItemInsertTextRule.InsertAsSnippet,
label: 'push()',
range,
},
{
detail: '',
kind: monaco.languages.CompletionItemKind.Method,
insertText: 'pop();',
insertTextRules: monaco.languages.CompletionItemInsertTextRule.InsertAsSnippet,
label: 'pop()',
range,
},
]
}
export function getAddressCompletionItems(range: IRange, monaco): monaco.languages.CompletionItem[] {
return [
{
detail: '(uint256): balance of the Address in Wei',
kind: monaco.languages.CompletionItemKind.Method,
insertText: 'balance;',
insertTextRules: monaco.languages.CompletionItemInsertTextRule.InsertAsSnippet,
label: 'balance',
range,
},
{
detail: '(bytes memory): code at the Address (can be empty)',
kind: monaco.languages.CompletionItemKind.Method,
insertText: 'code;',
insertTextRules: monaco.languages.CompletionItemInsertTextRule.InsertAsSnippet,
label: 'code',
range,
},
{
detail: '(bytes32): the codehash of the Address',
kind: monaco.languages.CompletionItemKind.Method,
insertText: 'codehash;',
insertTextRules: monaco.languages.CompletionItemInsertTextRule.InsertAsSnippet,
label: 'codehash',
range,
},
{
detail: '(uint256 amount) returns (bool): send given amount of Wei to Address, returns false on failure',
kind: monaco.languages.CompletionItemKind.Method,
insertText: 'send(${1:value});',
insertTextRules: monaco.languages.CompletionItemInsertTextRule.InsertAsSnippet,
label: 'send()',
range,
},
{
detail: '(uint256 amount): send given amount of Wei to Address, throws on failure',
kind: monaco.languages.CompletionItemKind.Method,
insertText: 'transfer(${1:value});',
insertTextRules: monaco.languages.CompletionItemInsertTextRule.InsertAsSnippet,
label: 'transfer()',
range,
},
]
}
export function getContextualAutoCompleteBTypeName(word: string, range: IRange, monaco): monaco.languages.CompletionItem[] {
if (word === 'ArrayTypeName') {
return getArrayCompletionItems(range, monaco);
}
if (word === 'bytes') {
return getBytesCompletionItems(range, monaco);
}
if (word === 'address') {
return getAddressCompletionItems(range, monaco);
}
return [];
}

@ -4,7 +4,7 @@ import { isArray } from "lodash"
import { editor, languages, Position } from "monaco-editor"
import monaco from "../../types/monaco"
import { EditorUIProps } from "../remix-ui-editor"
import { GeCompletionUnits, GetCompletionKeywords, getCompletionSnippets, GetCompletionTypes, getContextualAutoCompleteByGlobalVariable, GetGlobalFunctions, GetGlobalVariable } from "./completion/completionGlobals"
import { GeCompletionUnits, GetCompletionKeywords, getCompletionSnippets, GetCompletionTypes, getContextualAutoCompleteBTypeName, getContextualAutoCompleteByGlobalVariable, GetGlobalFunctions, GetGlobalVariable } from "./completion/completionGlobals"
export class RemixCompletionProvider implements languages.CompletionItemProvider {
@ -44,6 +44,9 @@ export class RemixCompletionProvider implements languages.CompletionItemProvider
let dotCompleted = false
// handles completion from for builtin types
if(lastNodeInExpression.memberName === 'sender') { // exception for this member
lastNodeInExpression.name = 'sender'
}
const globalCompletion = getContextualAutoCompleteByGlobalVariable(lastNodeInExpression.name, range, this.monaco)
if (globalCompletion) {
dotCompleted = true
@ -62,8 +65,9 @@ export class RemixCompletionProvider implements languages.CompletionItemProvider
if (expressionElements.length > 1 && !dotCompleted) {
const nameOfLastTypedExpression = lastNodeInExpression.name || lastNodeInExpression.memberName
nodes = [...nodes, ...await this.getDotCompletions(position, nameOfLastTypedExpression)]
const dotCompletions = await this.getDotCompletions(position, nameOfLastTypedExpression, range)
nodes = [...nodes, ...dotCompletions.nodes]
suggestions = [...suggestions, ...dotCompletions.suggestions]
}
} else {
@ -278,27 +282,6 @@ export class RemixCompletionProvider implements languages.CompletionItemProvider
return nodes;
}
private getContractAtPosition = async (position: Position) => {
const cursorPosition = this.props.editorAPI.getCursorPosition()
let nodesAtPosition = await this.props.plugin.call('codeParser', 'nodesAtPosition', cursorPosition)
console.log('NODES AT POSITION', nodesAtPosition)
// if no nodes exits at position, try to get the block of which the position is in
const ANTLRBlock = await this.props.plugin.call('codeParser', 'getANTLRBlockAtPosition', position, null)
if (!nodesAtPosition.length) {
if (ANTLRBlock) {
nodesAtPosition = await this.props.plugin.call('codeParser', 'nodesAtPosition', ANTLRBlock.range[0])
}
}
if (isArray(nodesAtPosition) && nodesAtPosition.length) {
for (const node of nodesAtPosition) {
if (node.nodeType === 'ContractDefinition') {
return node
}
}
}
return null
}
private getContractCompletions = async (position: Position) => {
let nodes: any[] = []
const cursorPosition = this.props.editorAPI.getCursorPosition()
@ -324,8 +307,6 @@ export class RemixCompletionProvider implements languages.CompletionItemProvider
nodes = [...Object.values(contractNodes.contractScopeNodes), ...nodes]
nodes = [...Object.values(contractNodes.baseNodesWithBaseContractScope), ...nodes]
nodes = [...Object.values(fileNodes.imports), ...nodes]
// some types like Enum and Events are not scoped so we need to add them manually
nodes = [...contractNodes.contractDefinition.nodes, ...nodes]
// at the nodes at the block itself
nodes = [...nodes, ...await this.getBlockNodesAtPosition(position)]
// filter private nodes, only allow them when contract ID is the same as the current contract
@ -371,9 +352,10 @@ export class RemixCompletionProvider implements languages.CompletionItemProvider
return nodes
}
private getDotCompletions = async (position: Position, nameOfLastTypedExpression: string) => {
private getDotCompletions = async (position: Position, nameOfLastTypedExpression: string, range) => {
const contractCompletions = await this.getContractCompletions(position)
let nodes: any[] = []
let suggestions: monaco.languages.CompletionItem[] = []
const filterNodes = (nodes: any[], parentNode: any, declarationOf: any = null) => {
return nodes && nodes.filter(node => {
@ -405,15 +387,20 @@ export class RemixCompletionProvider implements languages.CompletionItemProvider
nodes = [...nodes, ...filterNodes(baseContract.nodes, nodeOfScope)]
}
} else if (nodeOfScope.members) {
console.log('METHOD 1 HAS MEMBERS OF')
nodes = [...nodes, ...filterNodes(nodeOfScope.members, nodeOfScope)]
} else if (nodeOfScope.typeName && nodeOfScope.typeName.nodeType === 'ArrayTypeName') {
suggestions = [...suggestions, ...getContextualAutoCompleteBTypeName('ArrayTypeName', range, this.monaco)]
} else if(nodeOfScope.typeName && nodeOfScope.typeName.nodeType === 'ElementaryTypeName' && nodeOfScope.typeName.name === 'bytes') {
suggestions = [...suggestions, ...getContextualAutoCompleteBTypeName('bytes', range, this.monaco)]
} else if(nodeOfScope.typeName && nodeOfScope.typeName.nodeType === 'ElementaryTypeName' && nodeOfScope.typeName.name === 'address') {
suggestions = [...suggestions, ...getContextualAutoCompleteBTypeName('address', range, this.monaco)]
}
}
}
return nodes
return { nodes, suggestions }
}
private getlinearizedBaseContracts = async (node: any) => {

Loading…
Cancel
Save