pull/5370/head
filip mertens 3 years ago
parent 25777c31ea
commit e4b92d8f64
  1. 47
      libs/remix-core-plugin/src/lib/editor-context-listener.ts
  2. 142
      libs/remix-ui/editor/src/lib/providers/hoverProvider.ts
  3. 147
      libs/remix-ui/editor/src/lib/remix-ui-editor.tsx

@ -2,11 +2,10 @@
import { Plugin } from '@remixproject/engine'
import { sourceMappingDecoder } from '@remix-project/remix-debug'
import { AstNode } from '@remix-project/remix-solidity-ts'
const profile = {
name: 'contextualListener',
methods: ['definitionAtPosition', 'jumpToDefinition', 'referrencesAtPosition', 'nodesAtEditorPosition', 'referencesOf', 'getActiveHighlights', 'gasEstimation', 'declarationOf', 'jumpToPosition'],
methods: ['getNodeById', 'positionOfDefinition', 'definitionAtPosition', 'jumpToDefinition', 'referrencesAtPosition', 'nodesAtEditorPosition', 'referencesOf', 'getActiveHighlights', 'gasEstimation', 'declarationOf', 'jumpToPosition'],
events: [],
version: '0.0.1'
}
@ -95,7 +94,7 @@ export class EditorContextListener extends Plugin {
return null
}
referencesOf(node: AstNode) {
referencesOf(node: any) {
const results = []
const highlights = (id) => {
if (this._index.Declarations && this._index.Declarations[id]) {
@ -118,8 +117,9 @@ export class EditorContextListener extends Plugin {
async nodesAtEditorPosition(position: any) {
const lastCompilationResult = await this.call('compilerArtefacts', 'getLastCompilationResult')
let urlFromPath = await this.call('fileManager', 'getUrlFromPath', this.currentFile)
if (lastCompilationResult && lastCompilationResult.languageversion.indexOf('soljson') === 0 && lastCompilationResult.data) {
const nodes = sourceMappingDecoder.nodesAtPosition(null, position, lastCompilationResult.data.sources[this.currentFile])
const nodes = sourceMappingDecoder.nodesAtPosition(null, position, lastCompilationResult.data.sources[this.currentFile] || lastCompilationResult.data.sources[urlFromPath.file])
return nodes
}
return []
@ -135,16 +135,20 @@ export class EditorContextListener extends Plugin {
}
}
async getNodeDefinition() {
async getNodeById(id: any) {
for (const key in this._index.FlatReferences) {
if (this._index.FlatReferences[key].id === id) {
return this._index.FlatReferences[key]
}
}
}
async definitionAtPosition(position: any) {
const nodes = await this.nodesAtEditorPosition(position)
console.log(nodes)
console.log(this._index.FlatReferences)
let nodeDefinition: AstNode
let node: AstNode
let nodeDefinition: any
let node: any
if (nodes && nodes.length) {
node = nodes[nodes.length - 1]
nodeDefinition = node
@ -165,23 +169,33 @@ export class EditorContextListener extends Plugin {
}
async jumpToDefinition(position: any) {
const node = await this.definitionAtPosition(position)
async positionOfDefinition(node: any) {
if (node) {
if (node.src) {
const position = sourceMappingDecoder.decode(node.src)
if (position) {
await this.jumpToPosition(position)
return position
}
}
}
return null
}
async jumpToDefinition(position: any) {
const node = await this.definitionAtPosition(position)
const sourcePosition = await this.positionOfDefinition(node)
if(sourcePosition){
await this.jumpToPosition(sourcePosition)
}
}
/*
/*
* onClick jump to position of ast node in the editor
*/
async jumpToPosition(position: any) {
const jumpToLine = async (fileName: string, lineColumn: any) => {
if (fileName !== await this.call('fileManager', 'file')) {
console.log('jump to file', fileName)
await this.call('fileManager', 'open', fileName)
}
if (lineColumn.start && lineColumn.start.line >= 0 && lineColumn.start.column >= 0) {
@ -189,6 +203,8 @@ export class EditorContextListener extends Plugin {
}
}
const lastCompilationResult = await this.call('compilerArtefacts', 'getLastCompilationResult')
console.log(lastCompilationResult.getSourceCode().sources)
console.log(position)
if (lastCompilationResult && lastCompilationResult.languageversion.indexOf('soljson') === 0 && lastCompilationResult.data) {
const lineColumn = await this.call('offsetToLineColumnConverter', 'offsetToLineColumn',
position,
@ -197,6 +213,7 @@ export class EditorContextListener extends Plugin {
lastCompilationResult.getAsts())
const filename = lastCompilationResult.getSourceName(position.file)
// TODO: refactor with rendererAPI.errorClick
console.log(filename, lineColumn)
jumpToLine(filename, lineColumn)
}
}
@ -206,10 +223,10 @@ export class EditorContextListener extends Plugin {
this._stopHighlighting()
this.currentPosition = cursorPosition
this.currentFile = file
if (compilationResult && compilationResult.data && compilationResult.data.sources[file]) {
const nodes = sourceMappingDecoder.nodesAtPosition(null, cursorPosition, compilationResult.data.sources[file])
let urlFromPath = await this.call('fileManager', 'getUrlFromPath', this.currentFile)
if (compilationResult && compilationResult.data && (compilationResult.data.sources[file] || compilationResult.data.sources[urlFromPath.file])) {
const nodes = sourceMappingDecoder.nodesAtPosition(null, cursorPosition, compilationResult.data.sources[file] || compilationResult.data.sources[urlFromPath.file])
this.nodes = nodes
console.log(nodes)
if (nodes && nodes.length && nodes[nodes.length - 1]) {
await this._highlightExpressions(nodes[nodes.length - 1], compilationResult)
}

@ -0,0 +1,142 @@
import monaco from '../../types/monaco'
export class RemixHoverProvider implements monaco.languages.HoverProvider {
props: any
constructor(props: any) {
this.props = props
}
provideHover = async function (model: any, position: monaco.Position) {
const cursorPosition = this.propseditorAPI.getHoverPosition(position)
const nodeDefinition = await this.propsplugin.call('contextualListener', 'definitionAtPosition', cursorPosition)
console.log(nodeDefinition)
const contents = []
const getDocs = async (node: any) => {
if (node.documentation && node.documentation.text) {
let text = ''
node.documentation.text.split('\n').forEach(line => {
text += `${line.trim()}\n`
})
contents.push({
value: text
})
}
}
const getLinks = async (node: any) => {
const position = await this.propsplugin.call('contextualListener', 'positionOfDefinition', node)
const lastCompilationResult = await this.propsplugin.call('compilerArtefacts', 'getLastCompilationResult')
const filename = lastCompilationResult.getSourceName(position.file)
console.log(filename, position)
const lineColumn = await this.propsplugin.call('offsetToLineColumnConverter', 'offsetToLineColumn',
position,
position.file,
lastCompilationResult.getSourceCode().sources,
lastCompilationResult.getAsts())
contents.push({
value: `${filename} ${lineColumn.start.line}:${lineColumn.start.column}`
})
}
const getVariableDeclaration = async (node: any) => {
if (node.typeDescriptions && node.typeDescriptions.typeString) {
return `${node.typeDescriptions.typeString}${node.name && node.name.length ? ` ${node.name}` : ''}`
}
}
const getParamaters = async (parameters: any) => {
if (parameters && parameters.parameters) {
let params = []
for (const param of parameters.parameters) {
params.push(await getVariableDeclaration(param))
}
return `(${params.join(', ')})`
}
}
const getOverrides = async (node: any) => {
if (node.overrides) {
let overrides = []
for (const override of node.overrides.overrides) {
overrides.push(override.name)
}
if (overrides.length)
return ` overrides (${overrides.join(', ')})`
return ''
}
}
const getlinearizedBaseContracts = async (node: any) => {
let params = []
for (const id of node.linearizedBaseContracts) {
const baseContract = await this.propsplugin.call('contextualListener', 'getNodeById', id)
params.push(
baseContract.name
)
}
if (params.length)
return `is ${params.join(', ')}`
return ''
}
if (!nodeDefinition) return null
if (nodeDefinition.absolutePath) {
const target = await this.propsplugin.call('fileManager', 'getPathFromUrl', nodeDefinition.absolutePath)
if (target.file !== nodeDefinition.absolutePath) {
contents.push({
value: `${target.file}`
})
}
contents.push({
value: `${nodeDefinition.absolutePath}`
})
}
if (nodeDefinition.typeDescriptions && nodeDefinition.nodeType === 'VariableDeclaration') {
contents.push({
value: await getVariableDeclaration(nodeDefinition)
})
}
else if (nodeDefinition.typeDescriptions && nodeDefinition.nodeType === 'ElementaryTypeName') {
contents.push({
value: `${nodeDefinition.typeDescriptions.typeString}`
})
} else if (nodeDefinition.nodeType === 'FunctionDefinition') {
contents.push({
value: `function ${nodeDefinition.name} ${await getParamaters(nodeDefinition.parameters)} ${nodeDefinition.visibility} ${nodeDefinition.stateMutability}${await getOverrides(nodeDefinition)} returns ${await getParamaters(nodeDefinition.returnParameters)}`
})
getDocs(nodeDefinition)
} else if (nodeDefinition.nodeType === 'ContractDefinition') {
contents.push({
value: `${nodeDefinition.contractKind} ${nodeDefinition.name} ${await getlinearizedBaseContracts(nodeDefinition)}`
})
getDocs(nodeDefinition)
} else {
contents.push({
value: `${nodeDefinition.nodeType}`
})
getDocs(nodeDefinition)
}
getLinks(nodeDefinition)
for (const key in contents) {
contents[key].value = '```remix-solidity\n' + contents[key].value + '\n```'
}
return {
range: new monaco.Range(
position.lineNumber,
position.column,
position.lineNumber,
model.getLineMaxColumn(position.lineNumber)
),
contents: contents
};
}
}

@ -10,6 +10,7 @@ import { loadTypes } from './web-types'
import monaco from '../types/monaco'
import { IPosition, languages } from 'monaco-editor'
import { sourceMappingDecoder } from '@remix-project/remix-debug'
import { RemixHoverProvider } from './providers/hoverProvider'
type cursorPosition = {
startLineNumber: number,
@ -435,6 +436,7 @@ export const EditorUI = (props: EditorUIProps) => {
monacoRef.current.languages.registerReferenceProvider('remix-solidity', {
async provideReferences(model: monaco.editor.ITextModel, position: monaco.Position, context: any, token: monaco.CancellationToken) {
const cursorPosition = props.editorAPI.getCursorPosition()
const nodes = await props.plugin.call('contextualListener', 'referrencesAtPosition', cursorPosition)
const references = []
@ -471,91 +473,138 @@ export const EditorUI = (props: EditorUIProps) => {
}
})
monacoRef.current.languages.registerHoverProvider('remix-solidity', {
provideHover: async function (model: any, position: monaco.Position) {
console.log('--------------------')
const cursorPosition = props.editorAPI.getHoverPosition(position)
const nodeDefinition = await props.plugin.call('contextualListener', 'definitionAtPosition', cursorPosition)
console.log(nodeDefinition)
const contents = []
const getDocs = async (node: any) => {
if(node.documentation && node.documentation.text) {
let text = ''
node.documentation.text.split('\n').forEach(line => {
text += `${line.trim()}\n`
})
if (node.documentation && node.documentation.text) {
let text = ''
node.documentation.text.split('\n').forEach(line => {
text += `${line.trim()}\n`
})
contents.push({
value: text
})
}
}
const getLinks = async (node: any) => {
const position = await props.plugin.call('contextualListener', 'positionOfDefinition', node)
const lastCompilationResult = await props.plugin.call('compilerArtefacts', 'getLastCompilationResult')
const filename = lastCompilationResult.getSourceName(position.file)
console.log(filename, position)
const lineColumn = await props.plugin.call('offsetToLineColumnConverter', 'offsetToLineColumn',
position,
position.file,
lastCompilationResult.getSourceCode().sources,
lastCompilationResult.getAsts())
contents.push({
value: text
value: `${filename} ${lineColumn.start.line}:${lineColumn.start.column}`
})
}
}
const getVariableDeclaration = async (node: any) => {
if(node.typeDescriptions && node.typeDescriptions.typeString) {
return `${node.typeDescriptions.typeString} ${node.name}`
}
if (node.typeDescriptions && node.typeDescriptions.typeString) {
return `${node.typeDescriptions.typeString}${node.name && node.name.length ? ` ${node.name}` : ''}`
}
}
const getParamaters = async (parameters: any) => {
if (parameters && parameters.parameters) {
let params = []
for (const param of parameters.parameters) {
params.push(await getVariableDeclaration(param))
}
return `(${params.join(', ')})`
}
}
const getOverrides = async (node: any) => {
if (node.overrides) {
let overrides = []
for (const override of node.overrides.overrides) {
overrides.push(override.name)
}
if (overrides.length)
return ` overrides (${overrides.join(', ')})`
return ''
}
}
const getParamaters = async (node: any) => {
if(node.parameters && node.parameters.parameters) {
const getlinearizedBaseContracts = async (node: any) => {
let params = []
for(const param of node.parameters.parameters) {
params.push(await getVariableDeclaration(param))
for (const id of node.linearizedBaseContracts) {
const baseContract = await props.plugin.call('contextualListener', 'getNodeById', id)
params.push(
baseContract.name
)
}
return `(${params.join(', ')})`
}
if (params.length)
return `is ${params.join(', ')}`
return ''
}
if (!nodeDefinition) return null
if (nodeDefinition.absolutePath) {
const target = await props.plugin.call('fileManager', 'getPathFromUrl', nodeDefinition.absolutePath)
if (target.file !== nodeDefinition.absolutePath) {
const target = await props.plugin.call('fileManager', 'getPathFromUrl', nodeDefinition.absolutePath)
if (target.file !== nodeDefinition.absolutePath) {
contents.push({
value: `${target.file}`
})
}
contents.push({
value: `${target.file}`
value: `${nodeDefinition.absolutePath}`
})
}
contents.push({
value: `${nodeDefinition.absolutePath}`
})
}
if (nodeDefinition.typeDescriptions && nodeDefinition.nodeType === 'VariableDeclaration') {
contents.push({
value: await getVariableDeclaration(nodeDefinition)
})
contents.push({
value: await getVariableDeclaration(nodeDefinition)
})
}
else if (nodeDefinition.typeDescriptions && nodeDefinition.nodeType === 'ElementaryTypeName') {
contents.push({
value: `${nodeDefinition.typeDescriptions.typeString}`
})
contents.push({
value: `${nodeDefinition.typeDescriptions.typeString}`
})
} else if (nodeDefinition.nodeType === 'FunctionDefinition') {
contents.push({
value: `(${nodeDefinition.visibility} function) ${nodeDefinition.name}: ${await getParamaters(nodeDefinition)}`
})
getDocs(nodeDefinition)
contents.push({
value: `function ${nodeDefinition.name} ${await getParamaters(nodeDefinition.parameters)} ${nodeDefinition.visibility} ${nodeDefinition.stateMutability}${await getOverrides(nodeDefinition)} returns ${await getParamaters(nodeDefinition.returnParameters)}`
})
getDocs(nodeDefinition)
} else if (nodeDefinition.nodeType === 'ContractDefinition') {
contents.push({
value: `${nodeDefinition.contractKind} ${nodeDefinition.name} ${await getlinearizedBaseContracts(nodeDefinition)}`
})
getDocs(nodeDefinition)
} else {
contents.push({
value: `${nodeDefinition.nodeType}`
})
getDocs(nodeDefinition)
contents.push({
value: `${nodeDefinition.nodeType}`
})
getDocs(nodeDefinition)
}
getLinks(nodeDefinition)
for (const key in contents) {
contents[key].value = '```remix-solidity\n' + contents[key].value + '\n```'
contents[key].value = '```remix-solidity\n' + contents[key].value + '\n```'
}
return {
range: new monaco.Range(
position.lineNumber,
position.column,
position.lineNumber,
model.getLineMaxColumn(position.lineNumber)
),
contents: contents
range: new monaco.Range(
position.lineNumber,
position.column,
position.lineNumber,
model.getLineMaxColumn(position.lineNumber)
),
contents: contents
};
}
}
})
loadTypes(monacoRef.current)

Loading…
Cancel
Save