From 481ed9b795c34e91c7f68f6dae37cad403629ada Mon Sep 17 00:00:00 2001 From: filip mertens Date: Tue, 14 Jun 2022 12:55:18 +0200 Subject: [PATCH] mv functions --- apps/remix-ide/src/app/files/fileSystem.ts | 4 +- .../src/lib/editor-context-listener.ts | 7 - .../lib/offset-line-to-column-converter.ts | 3 - .../providers/completion/completionGlobals.ts | 375 +++++++++++++++++- .../providers/completion/dotCompletions.ts | 0 .../src/lib/providers/completionProvider.ts | 178 +++++---- .../editor/src/lib/providers/hoverProvider.ts | 2 +- .../src/types/{monaco.d.ts => monaco.ts} | 0 libs/remix-ui/editor/tsconfig.lib.json | 2 +- 9 files changed, 468 insertions(+), 103 deletions(-) create mode 100644 libs/remix-ui/editor/src/lib/providers/completion/dotCompletions.ts rename libs/remix-ui/editor/src/types/{monaco.d.ts => monaco.ts} (100%) diff --git a/apps/remix-ide/src/app/files/fileSystem.ts b/apps/remix-ide/src/app/files/fileSystem.ts index 80b7663680..033a1679e0 100644 --- a/apps/remix-ide/src/app/files/fileSystem.ts +++ b/apps/remix-ide/src/app/files/fileSystem.ts @@ -19,9 +19,10 @@ export class fileSystem { checkWorkspaces = async () => { try { await this.fs.stat('.workspaces') + console.log('Workspaces found in', this.name) this.hasWorkSpaces = true } catch (e) { - + console.log('No workspaces found in', this.name) } } @@ -49,6 +50,7 @@ export class fileSystems { return true } catch (e) { console.log(fs.name + ' not available...') + console.log(e) return false } } 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 a73e68cfbd..2bd39a7042 100644 --- a/libs/remix-core-plugin/src/lib/editor-context-listener.ts +++ b/libs/remix-core-plugin/src/lib/editor-context-listener.ts @@ -75,17 +75,10 @@ export class EditorContextListener extends Plugin { this.on('solidity', 'loadingCompiler', async (url) => { console.log('loading compiler', url) - this.compiler.loadVersion(true, url) this.compiler.event.register('compilerLoaded', async () => { console.log('compiler loaded') - //await this.compile() - //await this.getAST() - const a = 'function (){a=4; c.getBook().run.}' - const ast = (SolidityParser as any).parse(a, { loc: true, range: true, tolerant: true }) - console.log('BAD AST', ast) }) - }) this.compiler = new Compiler((url, cb) => this.call('contentImport', 'resolveAndSave', url, undefined, false).then((result) => cb(null, result)).catch((error) => cb(error.message))) diff --git a/libs/remix-core-plugin/src/lib/offset-line-to-column-converter.ts b/libs/remix-core-plugin/src/lib/offset-line-to-column-converter.ts index 1dc883e7d3..359b9c6549 100644 --- a/libs/remix-core-plugin/src/lib/offset-line-to-column-converter.ts +++ b/libs/remix-core-plugin/src/lib/offset-line-to-column-converter.ts @@ -30,12 +30,10 @@ export class OffsetToLineColumnConverter extends Plugin { * @param {Object.} asts - Map of content sources */ offsetToLineColumn (rawLocation, file, sources, asts) { - console.log('offsetToLineColumn', rawLocation, file, sources, asts) //if (!this.lineBreakPositionsByContent[file]) { const sourcesArray = Object.keys(sources) if (!asts || (file === 0 && sourcesArray.length === 1)) { // if we don't have ast, we process the only one available content (applicable also for compiler older than 0.4.12) - console.log('convert ', sources[sourcesArray[0]].content) this.lineBreakPositionsByContent[file] = this.sourceMappingDecoder.getLinebreakPositions(sources[sourcesArray[0]].content) } else { @@ -48,7 +46,6 @@ export class OffsetToLineColumnConverter extends Plugin { } } //} - console.log(this.lineBreakPositionsByContent[file]) return this.sourceMappingDecoder.convertOffsetToLineColumn(rawLocation, this.lineBreakPositionsByContent[file]) } diff --git a/libs/remix-ui/editor/src/lib/providers/completion/completionGlobals.ts b/libs/remix-ui/editor/src/lib/providers/completion/completionGlobals.ts index 4cf11cfd71..9621d625c3 100644 --- a/libs/remix-ui/editor/src/lib/providers/completion/completionGlobals.ts +++ b/libs/remix-ui/editor/src/lib/providers/completion/completionGlobals.ts @@ -1,12 +1,12 @@ import { IRange } from "monaco-editor"; import monaco from "../../../types/monaco"; -export function getBlockCompletionItems(range: IRange): monaco.languages.CompletionItem[] { +export function getBlockCompletionItems(range: IRange, monaco): monaco.languages.CompletionItem[] { return [ { detail: '(address): Current block miner’s address', kind: monaco.languages.CompletionItemKind.Property, - insertText: 'miner', + insertText: 'coinbase', label: 'coinbase', range, }, @@ -14,6 +14,7 @@ export function getBlockCompletionItems(range: IRange): monaco.languages.Complet 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});', kind: monaco.languages.CompletionItemKind.Method, + insertTextRules: monaco.languages.CompletionItemInsertTextRule.InsertAsSnippet, label: 'blockhash', range }, @@ -48,7 +49,89 @@ export function getBlockCompletionItems(range: IRange): monaco.languages.Complet ]; } -export function getTxCompletionItems(range: IRange): monaco.languages.CompletionItem[] { +export function getCompletionSnippets(range: IRange, monaco): monaco.languages.CompletionItem[] { + return [ + { + label: 'contract', + kind: monaco.languages.CompletionItemKind.Snippet, + insertText: 'contract ${1:Name} {\n\t$0\n}', + insertTextRules: monaco.languages.CompletionItemInsertTextRule.InsertAsSnippet, + range + }, + { + label: 'library', + kind: monaco.languages.CompletionItemKind.Snippet, + insertText: 'library ${1:Name} {\n\t$0\n}', + insertTextRules: monaco.languages.CompletionItemInsertTextRule.InsertAsSnippet, + range + }, + { + label: 'interface', + kind: monaco.languages.CompletionItemKind.Snippet, + insertText: 'interface ${1:Name} {\n\t$0\n}', + insertTextRules: monaco.languages.CompletionItemInsertTextRule.InsertAsSnippet, + range + }, + { + label: 'enum', + kind: monaco.languages.CompletionItemKind.Snippet, + insertText: 'enum ${1:Name} {${2:item1}, ${3:item2} }', + insertTextRules: monaco.languages.CompletionItemInsertTextRule.InsertAsSnippet, + range + }, + { + label: 'function', + kind: monaco.languages.CompletionItemKind.Snippet, + insertText: 'function ${1:name}(${2:params}) {\n\t${3:code}\n}', + insertTextRules: monaco.languages.CompletionItemInsertTextRule.InsertAsSnippet, + range + }, + { + label: 'constructor', + kind: monaco.languages.CompletionItemKind.Snippet, + insertText: 'constructor(${1:params}) {\n\t${2:code}\n}', + insertTextRules: monaco.languages.CompletionItemInsertTextRule.InsertAsSnippet, + range + }, + { + label: 'ifstatement', + kind: monaco.languages.CompletionItemKind.Snippet, + insertText: 'if (${1:condition}) {\n\t${2:code}\n}', + insertTextRules: monaco.languages.CompletionItemInsertTextRule.InsertAsSnippet, + range + }, + { + label: 'ifstatementelse', + kind: monaco.languages.CompletionItemKind.Snippet, + insertText: 'if (${1:condition}) {\n\t${2:code}\n} else {\n\t${3:code}\n}', + insertTextRules: monaco.languages.CompletionItemInsertTextRule.InsertAsSnippet, + range + }, + { + label: 'pragma', + kind: monaco.languages.CompletionItemKind.Snippet, + insertText: '// SPDX-License-Identifier: MIT\npragma solidity ${1:version};', + insertTextRules: monaco.languages.CompletionItemInsertTextRule.InsertAsSnippet, + range + }, + { + label: 'import', + kind: monaco.languages.CompletionItemKind.Snippet, + insertText: 'import "${1:library}";', + insertTextRules: monaco.languages.CompletionItemInsertTextRule.InsertAsSnippet, + range + }, + { + label: 'SPDX-License-Identifier', + kind: monaco.languages.CompletionItemKind.Snippet, + insertText: '// SPDX-License-Identifier: MIT', + insertTextRules: monaco.languages.CompletionItemInsertTextRule.InsertAsSnippet, + range + } + ] +} + +export function getTxCompletionItems(range: IRange, monaco): monaco.languages.CompletionItem[] { return [ { detail: '(uint): gas price of the transaction', @@ -67,7 +150,7 @@ export function getTxCompletionItems(range: IRange): monaco.languages.Completion ]; } -export function getMsgCompletionItems(range: IRange): monaco.languages.CompletionItem[] { +export function getMsgCompletionItems(range: IRange, monaco): monaco.languages.CompletionItem[] { return [ { detail: '(bytes): complete calldata', @@ -107,12 +190,13 @@ export function getMsgCompletionItems(range: IRange): monaco.languages.Completio ]; } -export function getAbiCompletionItems(range: IRange): monaco.languages.CompletionItem[] { +export function getAbiCompletionItems(range: IRange, monaco): monaco.languages.CompletionItem[] { return [ { detail: 'encode(..) returs (bytes): ABI-encodes the given arguments', insertText: 'encode(${1:arg});', kind: monaco.languages.CompletionItemKind.Method, + insertTextRules: monaco.languages.CompletionItemInsertTextRule.InsertAsSnippet, label: 'encode', range }, @@ -120,6 +204,7 @@ export function getAbiCompletionItems(range: IRange): monaco.languages.Completio detail: 'encodePacked(..) returns (bytes): Performes packed encoding of the given arguments', insertText: 'encodePacked(${1:arg});', kind: monaco.languages.CompletionItemKind.Method, + insertTextRules: monaco.languages.CompletionItemInsertTextRule.InsertAsSnippet, label: 'encodePacked', range }, @@ -127,6 +212,7 @@ export function getAbiCompletionItems(range: IRange): monaco.languages.Completio detail: 'encodeWithSelector(bytes4,...) returns (bytes): ABI-encodes the given arguments starting from the second and prepends the given four-byte selector', insertText: 'encodeWithSelector(${1:bytes4}, ${2:arg});', kind: monaco.languages.CompletionItemKind.Method, + insertTextRules: monaco.languages.CompletionItemInsertTextRule.InsertAsSnippet, label: 'encodeWithSelector', range }, @@ -134,8 +220,285 @@ export function getAbiCompletionItems(range: IRange): monaco.languages.Completio detail: 'encodeWithSignature(string,...) returns (bytes): Equivalent to abi.encodeWithSelector(bytes4(keccak256(signature), ...)`', insertText: 'encodeWithSignature(${1:signatureString}, ${2:arg});', kind: monaco.languages.CompletionItemKind.Method, + insertTextRules: monaco.languages.CompletionItemInsertTextRule.InsertAsSnippet, label: 'encodeWithSignature', - range + range }, ]; +} + + +export function GetCompletionTypes(range: IRange, monaco): monaco.languages.CompletionItem[] { + const completionItems = []; + const types = ['address', 'string', 'bytes', 'byte', 'int', 'uint', 'bool', 'hash']; + for (let index = 8; index <= 256; index += 8) { + types.push('int' + index); + types.push('uint' + index); + types.push('bytes' + index / 8); + } + types.forEach(type => { + const completionItem = CreateCompletionItem(type, monaco.languages.CompletionItemKind.Keyword, type + ' type', range); + completionItems.push(completionItem); + }); + // add mapping + return completionItems; +} + +function CreateCompletionItem(label: string, kind: monaco.languages.CompletionItemKind, detail: string, range: IRange) { + const completionItem: monaco.languages.CompletionItem = { + label, + kind, + detail, + insertText: label, + range + } + completionItem.kind = kind; + completionItem.detail = detail; + return completionItem; +} + +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', + '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 => { + const completionItem: monaco.languages.CompletionItem = { + label: unit, + kind: monaco.languages.CompletionItemKind.Keyword, + detail: unit + ' keyword', + insertText: `${unit} `, + range + } + completionItems.push(completionItem); + }); + + 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('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)); + completionItems.push(CreateCompletionItem('immutable', monaco.languages.CompletionItemKind.Keyword, null, range)); + completionItems.push(CreateCompletionItem('constructor', monaco.languages.CompletionItemKind.Constructor, null, range)); + completionItems.push(CreateCompletionItem('event', monaco.languages.CompletionItemKind.Event, null, range)); + completionItems.push(CreateCompletionItem('import', monaco.languages.CompletionItemKind.Module, null, range)); + completionItems.push(CreateCompletionItem('enum', monaco.languages.CompletionItemKind.Enum, null, range)); + completionItems.push(CreateCompletionItem('struct', monaco.languages.CompletionItemKind.Struct, null, range)); + completionItems.push(CreateCompletionItem('function', monaco.languages.CompletionItemKind.Function, null, range)); + + return completionItems; +} + + +export function GeCompletionUnits(range: IRange, monaco): monaco.languages.CompletionItem[] { + const completionItems = []; + const etherUnits = ['wei', 'gwei', 'finney', 'szabo', 'ether']; + etherUnits.forEach(unit => { + const completionItem = CreateCompletionItem(unit, monaco.languages.CompletionItemKind.Unit, unit + ': ether unit', range); + completionItems.push(completionItem); + }); + + const timeUnits = ['seconds', 'minutes', 'hours', 'days', 'weeks', 'years']; + timeUnits.forEach(unit => { + const completionItem = CreateCompletionItem(unit, monaco.languages.CompletionItemKind.Unit, unit + ': time unit', range); + completionItem.kind = monaco.languages.CompletionItemKind.Unit; + + if (unit !== 'years') { + completionItem.detail = unit + ': time unit'; + } else { + completionItem.detail = 'DEPRECATED: ' + unit + ': time unit'; + } + completionItems.push(completionItem); + }); + + return completionItems; +} + +export function GetGlobalVariable(range: IRange, monaco): monaco.languages.CompletionItem[] { + return [ + { + detail: 'Current block', + kind: monaco.languages.CompletionItemKind.Variable, + insertText: 'block', + label: 'block', + range + }, + { + detail: 'Current Message', + kind: monaco.languages.CompletionItemKind.Variable, + insertText: 'msg', + label: 'msg', + range + }, + { + detail: '(uint): current block timestamp (alias for block.timestamp)', + kind: monaco.languages.CompletionItemKind.Variable, + insertText: 'now', + label: 'now', + range + }, + { + detail: 'Current transaction', + kind: monaco.languages.CompletionItemKind.Variable, + label: 'tx', + insertText: 'tx', + range + }, + { + detail: 'ABI encoding / decoding', + kind: monaco.languages.CompletionItemKind.Variable, + label: 'abi', + insertText: 'abi', + range + }, + { + detail: '', + kind: monaco.languages.CompletionItemKind.Variable, + label: 'this', + insertText: 'this', + range + }, + ]; +} + +export function GetGlobalFunctions(range: IRange, monaco): monaco.languages.CompletionItem[] { + return [ + { + detail: 'assert(bool condition): throws if the condition is not met - to be used for internal errors.', + insertText: 'assert(${1:condition});', + kind: monaco.languages.CompletionItemKind.Function, + insertTextRules: monaco.languages.CompletionItemInsertTextRule.InsertAsSnippet, + label: 'assert', + range + }, + { + detail: 'gasleft(): returns the remaining gas', + insertText: 'gasleft();', + kind: monaco.languages.CompletionItemKind.Function, + label: 'gasleft', + range + }, + { + detail: 'unicode: converts string into unicode', + insertText: 'unicode"${1:text}"', + kind: monaco.languages.CompletionItemKind.Function, + insertTextRules: monaco.languages.CompletionItemInsertTextRule.InsertAsSnippet, + label: 'unicode', + range + }, + { + detail: 'blockhash(uint blockNumber): hash of the given block - only works for 256 most recent, excluding current, blocks', + insertText: 'blockhash(${1:blockNumber});', + kind: monaco.languages.CompletionItemKind.Function, + insertTextRules: monaco.languages.CompletionItemInsertTextRule.InsertAsSnippet, + label: 'blockhash', + range + }, + { + detail: 'require(bool condition): reverts if the condition is not met - to be used for errors in inputs or external components.', + insertText: 'require(${1:condition});', + kind: monaco.languages.CompletionItemKind.Method, + insertTextRules: monaco.languages.CompletionItemInsertTextRule.InsertAsSnippet, + label: 'require', + range + }, + { + // tslint:disable-next-line:max-line-length + detail: 'require(bool condition, string message): reverts if the condition is not met - to be used for errors in inputs or external components. Also provides an error message.', + insertText: 'require(${1:condition}, ${2:message});', + kind: monaco.languages.CompletionItemKind.Method, + insertTextRules: monaco.languages.CompletionItemInsertTextRule.InsertAsSnippet, + label: 'require', + range + }, + { + detail: 'revert(): abort execution and revert state changes', + insertText: 'revert();', + kind: monaco.languages.CompletionItemKind.Method, + label: 'revert', + range + }, + { + detail: 'addmod(uint x, uint y, uint k) returns (uint):' + + 'compute (x + y) % k where the addition is performed with arbitrary precision and does not wrap around at 2**256', + insertText: 'addmod(${1:x}, ${2:y}, ${3:k})', + kind: monaco.languages.CompletionItemKind.Method, + insertTextRules: monaco.languages.CompletionItemInsertTextRule.InsertAsSnippet, + label: 'addmod', + range + }, + { + detail: 'mulmod(uint x, uint y, uint k) returns (uint):' + + 'compute (x * y) % k where the multiplication is performed with arbitrary precision and does not wrap around at 2**256', + insertText: 'mulmod(${1:x}, ${2:y}, ${3:k})', + kind: monaco.languages.CompletionItemKind.Method, + insertTextRules: monaco.languages.CompletionItemInsertTextRule.InsertAsSnippet, + label: 'mulmod', + range + }, + { + detail: 'keccak256(...) returns (bytes32):' + + 'compute the Ethereum-SHA-3 (Keccak-256) hash of the (tightly packed) arguments', + insertText: 'keccak256(${1:x})', + kind: monaco.languages.CompletionItemKind.Method, + insertTextRules: monaco.languages.CompletionItemInsertTextRule.InsertAsSnippet, + label: 'keccak256', + range + }, + { + detail: 'sha256(...) returns (bytes32):' + + 'compute the SHA-256 hash of the (tightly packed) arguments', + insertText: 'sha256(${1:x})', + insertTextRules: monaco.languages.CompletionItemInsertTextRule.InsertAsSnippet, + kind: monaco.languages.CompletionItemKind.Method, + label: 'sha256', + range + }, + { + detail: 'sha3(...) returns (bytes32):' + + 'alias to keccak256', + insertText: 'sha3(${1:x})', + insertTextRules: monaco.languages.CompletionItemInsertTextRule.InsertAsSnippet, + kind: monaco.languages.CompletionItemKind.Method, + label: 'sha3', + range + }, + { + detail: 'ripemd160(...) returns (bytes20):' + + 'compute RIPEMD-160 hash of the (tightly packed) arguments', + insertText: 'ripemd160(${1:x})', + insertTextRules: monaco.languages.CompletionItemInsertTextRule.InsertAsSnippet, + kind: monaco.languages.CompletionItemKind.Method, + label: 'ripemd160', + range + }, + { + detail: 'ecrecover(bytes32 hash, uint8 v, bytes32 r, bytes32 s) returns (address):' + + 'recover the address associated with the public key from elliptic curve signature or return zero on error', + insertText: 'ecrecover(${1:hash}, ${2:v}, ${3:r}, ${4:s})', + insertTextRules: monaco.languages.CompletionItemInsertTextRule.InsertAsSnippet, + kind: monaco.languages.CompletionItemKind.Method, + label: 'ecrecover', + range + }, + + ]; +} + +export function getContextualAutoCompleteByGlobalVariable(word: string, range: IRange, monaco): monaco.languages.CompletionItem[] { + if (word === 'block') { + return getBlockCompletionItems(range, monaco); + } + if (word === 'msg') { + return getMsgCompletionItems(range, monaco); + } + if (word === 'tx') { + return getTxCompletionItems(range, monaco); + } + if (word === 'address') { + return getAbiCompletionItems(range, monaco); + } + return null; } \ No newline at end of file diff --git a/libs/remix-ui/editor/src/lib/providers/completion/dotCompletions.ts b/libs/remix-ui/editor/src/lib/providers/completion/dotCompletions.ts new file mode 100644 index 0000000000..e69de29bb2 diff --git a/libs/remix-ui/editor/src/lib/providers/completionProvider.ts b/libs/remix-ui/editor/src/lib/providers/completionProvider.ts index f0c8dcc641..f3af4a9ddf 100644 --- a/libs/remix-ui/editor/src/lib/providers/completionProvider.ts +++ b/libs/remix-ui/editor/src/lib/providers/completionProvider.ts @@ -1,12 +1,13 @@ import { editor, languages, Position } from "monaco-editor" import monaco from "../../types/monaco" import { EditorUIProps } from "../remix-ui-editor" -import { getBlockCompletionItems } from "./completion/completionGlobals" +import { GeCompletionUnits, getBlockCompletionItems, GetCompletionKeywords, getCompletionSnippets, GetCompletionTypes, getContextualAutoCompleteByGlobalVariable, GetGlobalFunctions, GetGlobalVariable, getMsgCompletionItems, getTxCompletionItems } from "./completion/completionGlobals" export class RemixCompletionProvider implements languages.CompletionItemProvider { props: EditorUIProps monaco: any + constructor(props: any, monaco: any) { this.props = props this.monaco = monaco @@ -17,16 +18,6 @@ export class RemixCompletionProvider implements languages.CompletionItemProvider console.log('AUTOCOMPLETE', context) console.log(position) - const textUntilPosition = model.getValueInRange({ - startLineNumber: 1, - startColumn: 1, - endLineNumber: position.lineNumber, - endColumn: position.column - }); - - - - const word = model.getWordUntilPosition(position); const wordAt = model.getWordAtPosition(position); const range = { @@ -37,25 +28,10 @@ export class RemixCompletionProvider implements languages.CompletionItemProvider }; console.log('WORD', word) - const getlinearizedBaseContracts = async (node: any) => { - let params = [] - if (node.linearizedBaseContracts) { - for (const id of node.linearizedBaseContracts) { - if (id !== node.id) { - const baseContract = await this.props.plugin.call('contextualListener', 'getNodeById', id) - params = [...params, ...[baseContract]] - } - } - } - return params - } - const line = model.getLineContent(position.lineNumber) let nodes = [] - - - + let suggestions = [] const cursorPosition = this.props.editorAPI.getCursorPosition() console.log('cursor', cursorPosition) @@ -85,7 +61,6 @@ export class RemixCompletionProvider implements languages.CompletionItemProvider } } - const linesToCheck = [ textBeforeCursor.substring(0, textBeforeCursor.lastIndexOf('.')) + ".lastnode;", @@ -105,23 +80,30 @@ export class RemixCompletionProvider implements languages.CompletionItemProvider } } - console.log('lastNode found', lastNode) - - - console.log(textBeforeCursor, textAfterCursor) const splits = textBeforeCursor.split('.') console.log('splits', splits) - if (splits.length > 1) { + let dotCompleted = false + if (splits.length === 2) { + let globalCompletion = getContextualAutoCompleteByGlobalVariable(lastNode.name, range, this.monaco) + if (globalCompletion) { + dotCompleted = true + suggestions = [...suggestions, ...globalCompletion] + } + if (lastNode.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 lastWord = null let lineWithoutEdits = null // get word before last closing parentheses if (lastParentheses > -1 && lastClosingParentheses > -1) { @@ -130,7 +112,6 @@ export class RemixCompletionProvider implements languages.CompletionItemProvider // 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) @@ -168,7 +149,7 @@ export class RemixCompletionProvider implements languages.CompletionItemProvider 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) + const baseContracts = await this.getlinearizedBaseContracts(declarationOf) for (const baseContract of baseContracts) { nodes = [...nodes, ...baseContract.nodes] } @@ -195,7 +176,6 @@ 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) @@ -207,7 +187,7 @@ export class RemixCompletionProvider implements languages.CompletionItemProvider 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) + const baseContracts = await this.getlinearizedBaseContracts(declarationOf) for (const baseContract of baseContracts) { //nodes = [...nodes, ...baseContract.nodes] } @@ -217,54 +197,21 @@ export class RemixCompletionProvider implements languages.CompletionItemProvider } } } else { - const cursorPosition = this.props.editorAPI.getCursorPosition() - 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) - } - } - - // 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] - } - } - } + 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), + ] + nodes = [...nodes, ...await this.getContractCompletions(nodes, position)] - 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') } - console.log('WORD', word, wordAt) console.log('NODES', nodes) - console.log('NODES', Object.values(nodes)) - - const getLinks = async (node: any) => { const position = await this.props.plugin.call('contextualListener', 'positionOfDefinition', node) @@ -301,8 +248,8 @@ export class RemixCompletionProvider implements languages.CompletionItemProvider const completeParameters = async (parameters: any) => { if (parameters && parameters.parameters) { const params = [] - for (const param of parameters.parameters) { - params.push(param.name) + for (const key in parameters.parameters) { + params.push('${' + (key + 1) + ':' + parameters.parameters[key].name + '}') } return `(${params.join(', ')})` } @@ -320,7 +267,7 @@ export class RemixCompletionProvider implements languages.CompletionItemProvider } } - const suggestions = [] + for (const node of Object.values(nodes) as any[]) { if (!node.name) continue if (node.nodeType === 'VariableDeclaration') { @@ -333,10 +280,12 @@ export class RemixCompletionProvider implements languages.CompletionItemProvider } suggestions.push(completion) } else if (node.nodeType === 'FunctionDefinition') { + const completion = { label: { label: `"${node.name}"`, description: await getLinks(node), detail: ` -> ${node.name} ${await getParamaters(node.parameters)}` }, kind: this.monaco.languages.CompletionItemKind.Function, insertText: `${node.name}${await completeParameters(node.parameters)};`, + insertTextRules: this.monaco.languages.CompletionItemInsertTextRule.InsertAsSnippet, range: range, documentation: await getDocs(node) } @@ -391,14 +340,75 @@ export class RemixCompletionProvider implements languages.CompletionItemProvider documentation: await getDocs(node) } suggestions.push(completion) + } else { + console.log('UNKNOWN NODE', node) } } - suggestions.push(getBlockCompletionItems(range)) + 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 } } + + private getlinearizedBaseContracts = async (node: any) => { + let params = [] + if (node.linearizedBaseContracts) { + for (const id of node.linearizedBaseContracts) { + if (id !== node.id) { + const baseContract = await this.props.plugin.call('contextualListener', 'getNodeById', id) + params = [...params, ...[baseContract]] + } + } + } + return params + } + + private getContractCompletions = async (nodes: any[], position: Position) => { + const cursorPosition = this.props.editorAPI.getCursorPosition() + let nodesAtPosition = await this.props.plugin.call('contextualListener', 'nodesAtEditorPosition', 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) + if (block) { + nodesAtPosition = await this.props.plugin.call('contextualListener', 'nodesAtEditorPosition', 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] + } + } + } + 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] + } + } + return nodes + } } \ No newline at end of file diff --git a/libs/remix-ui/editor/src/lib/providers/hoverProvider.ts b/libs/remix-ui/editor/src/lib/providers/hoverProvider.ts index 4fec34dd9c..45d346d417 100644 --- a/libs/remix-ui/editor/src/lib/providers/hoverProvider.ts +++ b/libs/remix-ui/editor/src/lib/providers/hoverProvider.ts @@ -13,7 +13,7 @@ export class RemixHoverProvider implements languages.HoverProvider { provideHover = async function (model: editor.ITextModel, position: Position) { console.log('HOVERING') - return null + 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/types/monaco.d.ts b/libs/remix-ui/editor/src/types/monaco.ts similarity index 100% rename from libs/remix-ui/editor/src/types/monaco.d.ts rename to libs/remix-ui/editor/src/types/monaco.ts diff --git a/libs/remix-ui/editor/tsconfig.lib.json b/libs/remix-ui/editor/tsconfig.lib.json index b560bc4dec..3bfb81979b 100644 --- a/libs/remix-ui/editor/tsconfig.lib.json +++ b/libs/remix-ui/editor/tsconfig.lib.json @@ -9,5 +9,5 @@ "../../../node_modules/@nrwl/react/typings/image.d.ts" ], "exclude": ["**/*.spec.ts", "**/*.spec.tsx"], - "include": ["**/*.js", "**/*.jsx", "**/*.ts", "**/*.tsx"] + "include": ["**/*.js", "**/*.jsx", "**/*.ts", "**/*.tsx", "**/*.d.ts"] }