add imports to autodcomplete

pull/2917/head
filip mertens 2 years ago
parent e5a65662c9
commit aeeb3d1cf1
  1. 268
      libs/remix-ui/editor/src/lib/providers/completion/completionGlobals.ts
  2. 131
      libs/remix-ui/editor/src/lib/providers/completionProvider.ts

@ -330,10 +330,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',
'after', 'promise', 'alias', 'apply','auto', 'copyof', 'default', 'define', 'final', 'implements',
'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',
'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 => {
@ -389,6 +389,268 @@ export function GeCompletionUnits(range: IRange, monaco): monaco.languages.Compl
return completionItems;
}
export function GetImports(range: IRange, monaco): monaco.languages.CompletionItem[] {
let list = [
{
kind: monaco.languages.CompletionItemKind.Module,
insertText: '@openzeppelin/contracts/token/ERC20/ERC20.sol',
label: 'OZ ERC20',
range
},
{
kind: monaco.languages.CompletionItemKind.Module,
insertText: '@openzeppelin/contracts/token/ERC721/ERC721.sol',
label: 'OZ ERC721',
range
},
{
kind: monaco.languages.CompletionItemKind.Module,
insertText: '@openzeppelin/contracts/access/Ownable.sol',
label: 'OZ Ownable',
range
},
{
kind: monaco.languages.CompletionItemKind.Module,
insertText: '@openzeppelin/contracts/utils/Counters.sol',
label: 'OZ Counters',
range
},
{
kind: monaco.languages.CompletionItemKind.Module,
insertText: '@openzeppelin/contracts/utils/Address.sol',
label: 'OZ Address',
range
},
{
detail: '@openzeppelin/contracts/utils/Context.sol',
kind: monaco.languages.CompletionItemKind.Module,
insertText: '@openzeppelin/contracts/utils/Context.sol',
label: 'OZ Context',
range
},
{
detail: '@openzeppelin/contracts/utils/EnumerableSet.sol',
kind: monaco.languages.CompletionItemKind.Module,
insertText: '@openzeppelin/contracts/utils/EnumerableSet.sol',
label: 'OZ EnumerableSet',
range
},
{
detail: '@openzeppelin/contracts/utils/EnumerableMap.sol',
kind: monaco.languages.CompletionItemKind.Module,
insertText: '@openzeppelin/contracts/utils/EnumerableMap.sol',
label: 'OZ EnumerableMap',
range
},
{
kind: monaco.languages.CompletionItemKind.Module,
insertText: '@openzeppelin/contracts/utils/Strings.sol',
label: 'OZ Strings',
range
},
{
kind: monaco.languages.CompletionItemKind.Module,
insertText: '@openzeppelin/contracts/utils/ReentrancyGuard.sol',
label: 'OZ ReentrancyGuard',
range
},
{
kind: monaco.languages.CompletionItemKind.Module,
insertText: '@openzeppelin/contracts/utils/Pausable.sol',
label: 'OZ Pausable',
range
},
{
kind: monaco.languages.CompletionItemKind.Module,
insertText: '@openzeppelin/contracts/utils/structs/EnumerableSet.sol',
label: 'OZ EnumerableSet',
range
},
{
kind: monaco.languages.CompletionItemKind.Module,
insertText: '@openzeppelin/contracts/utils/structs/EnumerableMap.sol',
label: 'OZ EnumerableMap',
range
},{
kind: monaco.languages.CompletionItemKind.Module,
insertText: '@openzeppelin/contracts/utils/math/SafeMath.sol',
label: 'OZ SafeMath',
range
},{
kind: monaco.languages.CompletionItemKind.Module,
insertText: '@openzeppelin/contracts/utils/math/SafeCast.sol',
label: 'OZ SafeCast',
range
},
{
kind: monaco.languages.CompletionItemKind.Module,
insertText: '@openzeppelin/contracts/utils/math/Math.sol',
label: 'OZ Math',
range
},{
kind: monaco.languages.CompletionItemKind.Module,
insertText: '@openzeppelin/contracts/utils/math/SignedSafeMath.sol',
label: 'OZ SignedSafeMath',
range
},
{
kind: monaco.languages.CompletionItemKind.Module,
insertText: '@openzeppelin/contracts/utils/math/SafeMath.sol',
label: 'OZ SafeMath',
range
},{
kind: monaco.languages.CompletionItemKind.Module,
insertText: '@openzeppelin/contracts/utils/math/SafeCast.sol',
label: 'OZ SafeCast',
range
},{
kind: monaco.languages.CompletionItemKind.Module,
insertText: '@openzeppelin/contracts/token/ERC20/extensions/ERC20Burnable.sol',
label: 'OZ ERC20Burnable',
range
},
{
kind: monaco.languages.CompletionItemKind.Module,
insertText: '@openzeppelin/contracts/token/ERC20/extensions/ERC20Pausable.sol',
label: 'OZ ERC20Pausable',
range
},
{
kind: monaco.languages.CompletionItemKind.Module,
insertText: '@openzeppelin/contracts/token/ERC20/extensions/ERC20Snapshot.sol',
label: 'OZ ERC20Snapshot',
range
},
{
kind: monaco.languages.CompletionItemKind.Module,
insertText: '@openzeppelin/contracts/token/ERC20/extensions/draft-ERC20Permit.sol',
label: 'OZ ERC20Permit',
range
},
{
kind: monaco.languages.CompletionItemKind.Module,
insertText: '@openzeppelin/contracts/token/ERC20/extensions/draft-ERC20Votes.sol',
label: 'OZ ERC20Votes',
range
},
{
kind: monaco.languages.CompletionItemKind.Module,
insertText: '@openzeppelin/contracts/token/ERC1155/ERC1155.sol',
label: 'OZ ERC1155',
range
},
{
kind: monaco.languages.CompletionItemKind.Module,
insertText: '@openzeppelin/contracts/token/ERC20/IERC20.sol',
label: 'OZ IERC20',
range
},
{
kind: monaco.languages.CompletionItemKind.Module,
insertText: '@openzeppelin/contracts/token/ERC721/IERC721.sol',
label: 'OZ IERC721',
range
},
{
kind: monaco.languages.CompletionItemKind.Module,
insertText: '@openzeppelin/contracts/token/ERC1155/IERC1155.sol',
label: 'OZ IERC1155',
range
},
{
kind: monaco.languages.CompletionItemKind.Module,
insertText: '@openzeppelin/contracts/token/ERC20/IERC20Metadata.sol',
label: 'OZ IERC20Metadata',
range
},
{
kind: monaco.languages.CompletionItemKind.Module,
insertText: '@openzeppelin/contracts/token/ERC721/IERC721Metadata.sol',
label: 'OZ IERC721Metadata',
range
},
{
kind: monaco.languages.CompletionItemKind.Module,
insertText: '@openzeppelin/contracts/token/ERC1155/IERC1155MetadataURI.sol',
label: 'OZ IERC1155MetadataURI',
range
},
{
kind: monaco.languages.CompletionItemKind.Module,
insertText: '@openzeppelin/contracts/token/ERC1155/IERC1155Receiver.sol',
label: 'OZ IERC1155Receiver',
range
},
{
kind: monaco.languages.CompletionItemKind.Module,
insertText: '@openzeppelin/contracts/token/ERC721/IERC721Receiver.sol',
label: 'OZ IERC721Receiver',
range
},
{
kind: monaco.languages.CompletionItemKind.Module,
insertText: '@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol',
label: 'OZ SafeERC20',
range
},
{
kind: monaco.languages.CompletionItemKind.Module,
insertText: '@openzeppelin/contracts/governance/Governor.sol',
label: 'OZ Governor',
range
},
{
kind: monaco.languages.CompletionItemKind.Module,
insertText: '@openzeppelin/contracts/governance/extensions/GovernorCountingSimple.sol',
label: 'OZ GovernorCountingSimple',
range
},{
kind: monaco.languages.CompletionItemKind.Module,
insertText: '@openzeppelin/contracts/governance/extensions/GovernorVotes.sol',
label: 'OZ GovernorVotes',
range
},{
kind: monaco.languages.CompletionItemKind.Module,
insertText: '@openzeppelin/contracts/governance/extensions/GovernorVotesQuorumFraction.sol',
label: 'OZ GovernorVotesQuorumFraction',
range
},{
kind: monaco.languages.CompletionItemKind.Module,
insertText: '@openzeppelin/contracts/governance/extensions/GovernorTimelockCompound.sol',
label: 'OZ GovernorTimelockCompound',
range
},{
kind: monaco.languages.CompletionItemKind.Module,
insertText: '@openzeppelin/contracts/governance/extensions/GovernorTimelockControl.sol',
label: 'OZ GovernorTimelockControl',
range
},{
kind: monaco.languages.CompletionItemKind.Module,
insertText: '@openzeppelin/contracts/governance/extensions/GovernorCountingSimple.sol',
label: 'OZ GovernorCountingSimple',
range
},{
kind: monaco.languages.CompletionItemKind.Module,
insertText: '@openzeppelin/contracts/governance/extensions/GovernorSettings.sol',
label: 'OZ GovernorSettings',
range
},{
kind: monaco.languages.CompletionItemKind.Module,
insertText: '@openzeppelin/contracts/governance/extensions/GovernorCompatibilityBravo.sol',
label: 'OZ GovernorCompatibilityBravo',
range
}
]
list = list.map((item) => {
return {
...item,
label: `${item.label}: ${item.insertText}`,
}
})
console.log(list)
return list;
};
export function GetGlobalVariable(range: IRange, monaco): monaco.languages.CompletionItem[] {
return [
@ -668,7 +930,7 @@ export function getAddressCompletionItems(range: IRange, monaco): monaco.languag
]
}
export function getContextualAutoCompleteBTypeName(word: string, range: IRange, monaco): monaco.languages.CompletionItem[] {
if (word === 'ArrayTypeName') {
return getArrayCompletionItems(range, monaco);

@ -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, getContextualAutoCompleteBTypeName, getContextualAutoCompleteByGlobalVariable, GetGlobalFunctions, GetGlobalVariable } from "./completion/completionGlobals"
import { GeCompletionUnits, GetCompletionKeywords, getCompletionSnippets, GetCompletionTypes, getContextualAutoCompleteBTypeName, getContextualAutoCompleteByGlobalVariable, GetGlobalFunctions, GetGlobalVariable, GetImports } from "./completion/completionGlobals"
export class RemixCompletionProvider implements languages.CompletionItemProvider {
@ -16,11 +16,11 @@ export class RemixCompletionProvider implements languages.CompletionItemProvider
this.monaco = monaco
}
triggerCharacters = ['.', '']
triggerCharacters = ['.', '', '"']
async provideCompletionItems(model: editor.ITextModel, position: Position, context: monaco.languages.CompletionContext): Promise<monaco.languages.CompletionList | undefined> {
const completionSettings = await this.props.plugin.call('config', 'getAppParameter', 'settings/auto-completion')
if(!completionSettings) return
if (!completionSettings) return
const word = model.getWordUntilPosition(position);
const range = {
startLineNumber: position.lineNumber,
@ -33,62 +33,75 @@ export class RemixCompletionProvider implements languages.CompletionItemProvider
let nodes: AstNode[] = []
let suggestions: monaco.languages.CompletionItem[] = []
if (context.triggerCharacter === '.') {
const lineTextBeforeCursor: string = line.substring(0, position.column - 1)
const lastNodeInExpression = await this.getLastNodeInExpression(lineTextBeforeCursor)
const expressionElements = lineTextBeforeCursor.split('.')
if (context.triggerCharacter === '"') {
let dotCompleted = false
const ast = await this.props.plugin.call('codeParser', 'parseSolidity', line)
if (ast && ast.children && ast.children[0] && ast.children[0].type === "ImportDirective") {
suggestions = [...suggestions,
...GetImports(range, this.monaco),
]
// handles completion from for builtin types
if(lastNodeInExpression.memberName === 'sender') { // exception for this member
lastNodeInExpression.name = 'sender'
}else{
return
}
const globalCompletion = getContextualAutoCompleteByGlobalVariable(lastNodeInExpression.name, range, this.monaco)
if (globalCompletion) {
dotCompleted = true
suggestions = [...suggestions, ...globalCompletion]
setTimeout(() => {
// eslint-disable-next-line no-debugger
// debugger
}, 2000)
}
// handle completion for global THIS.
if (lastNodeInExpression.name === 'this') {
dotCompleted = true
nodes = [...nodes, ...await this.getThisCompletions(position)]
}
// handle completion for other dot completions
if (expressionElements.length > 1 && !dotCompleted) {
} else
const nameOfLastTypedExpression = lastNodeInExpression.name || lastNodeInExpression.memberName
const dotCompletions = await this.getDotCompletions(position, nameOfLastTypedExpression, range)
nodes = [...nodes, ...dotCompletions.nodes]
suggestions = [...suggestions, ...dotCompletions.suggestions]
}
} else {
if (context.triggerCharacter === '.') {
const lineTextBeforeCursor: string = line.substring(0, position.column - 1)
const lastNodeInExpression = await this.getLastNodeInExpression(lineTextBeforeCursor)
const expressionElements = lineTextBeforeCursor.split('.')
// handles contract completions and other suggestions
suggestions = [...suggestions,
...GetGlobalVariable(range, this.monaco),
...getCompletionSnippets(range, this.monaco),
...GetCompletionTypes(range, this.monaco),
...GetCompletionKeywords(range, this.monaco),
...GetGlobalFunctions(range, this.monaco),
...GeCompletionUnits(range, this.monaco),
]
let contractCompletions = await this.getContractCompletions(position)
let dotCompleted = false
// we can't have external nodes without using this.
contractCompletions = contractCompletions.filter(node => {
if (node.visibility && node.visibility === 'external') {
return false
// handles completion from for builtin types
if (lastNodeInExpression.memberName === 'sender') { // exception for this member
lastNodeInExpression.name = 'sender'
}
return true
})
nodes = [...nodes, ...contractCompletions]
const globalCompletion = getContextualAutoCompleteByGlobalVariable(lastNodeInExpression.name, range, this.monaco)
if (globalCompletion) {
dotCompleted = true
suggestions = [...suggestions, ...globalCompletion]
setTimeout(() => {
// eslint-disable-next-line no-debugger
// debugger
}, 2000)
}
// handle completion for global THIS.
if (lastNodeInExpression.name === 'this') {
dotCompleted = true
nodes = [...nodes, ...await this.getThisCompletions(position)]
}
// handle completion for other dot completions
if (expressionElements.length > 1 && !dotCompleted) {
}
const nameOfLastTypedExpression = lastNodeInExpression.name || lastNodeInExpression.memberName
const dotCompletions = await this.getDotCompletions(position, nameOfLastTypedExpression, range)
nodes = [...nodes, ...dotCompletions.nodes]
suggestions = [...suggestions, ...dotCompletions.suggestions]
}
} else {
// handles contract completions and other suggestions
suggestions = [...suggestions,
...GetGlobalVariable(range, this.monaco),
...getCompletionSnippets(range, this.monaco),
...GetCompletionTypes(range, this.monaco),
...GetCompletionKeywords(range, this.monaco),
...GetGlobalFunctions(range, this.monaco),
...GeCompletionUnits(range, this.monaco),
]
let contractCompletions = await this.getContractCompletions(position)
// we can't have external nodes without using this.
contractCompletions = contractCompletions.filter(node => {
if (node.visibility && node.visibility === 'external') {
return false
}
return true
})
nodes = [...nodes, ...contractCompletions]
}
// remove duplicates
const nodeIds = {};
@ -220,7 +233,7 @@ export class RemixCompletionProvider implements languages.CompletionItemProvider
}
suggestions.push(completion)
}
}
}
return {
@ -242,9 +255,9 @@ export class RemixCompletionProvider implements languages.CompletionItemProvider
// try to find the real block in the AST and get the nodes in that scope
if (node.nodeType === 'ContractDefinition') {
const contractNodes = fileNodes.contracts[node.name].contractNodes
for (const contractNode of Object.values(contractNodes)) {
if (contractNode['name'] === ANTLRBlock.name
|| (contractNode['kind'] === 'constructor' && ANTLRBlock.name === null )
for (const contractNode of Object.values(contractNodes)) {
if (contractNode['name'] === ANTLRBlock.name
|| (contractNode['kind'] === 'constructor' && ANTLRBlock.name === null)
) {
let nodeOfScope = await this.props.plugin.call('codeParser', 'getNodesWithScope', (contractNode as any).id)
nodes = [...nodes, ...nodeOfScope]
@ -270,7 +283,7 @@ export class RemixCompletionProvider implements languages.CompletionItemProvider
*/
}
}
// we are only interested in nodes that are in the same block as the cursor
nodes = nodes.filter(node => {
if (node.src) {
@ -279,7 +292,7 @@ export class RemixCompletionProvider implements languages.CompletionItemProvider
return true
}
}
if(node.outSideBlock){ return true }
if (node.outSideBlock) { return true }
return false
})
@ -387,9 +400,9 @@ export class RemixCompletionProvider implements languages.CompletionItemProvider
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') {
} 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') {
} else if (nodeOfScope.typeName && nodeOfScope.typeName.nodeType === 'ElementaryTypeName' && nodeOfScope.typeName.name === 'address') {
suggestions = [...suggestions, ...getContextualAutoCompleteBTypeName('address', range, this.monaco)]
}
}

Loading…
Cancel
Save