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 { Plugin } from '@remixproject/engine'
import { sourceMappingDecoder } from '@remix-project/remix-debug' import { sourceMappingDecoder } from '@remix-project/remix-debug'
import { AstNode } from '@remix-project/remix-solidity-ts'
const profile = { const profile = {
name: 'contextualListener', 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: [], events: [],
version: '0.0.1' version: '0.0.1'
} }
@ -95,7 +94,7 @@ export class EditorContextListener extends Plugin {
return null return null
} }
referencesOf(node: AstNode) { referencesOf(node: any) {
const results = [] const results = []
const highlights = (id) => { const highlights = (id) => {
if (this._index.Declarations && this._index.Declarations[id]) { if (this._index.Declarations && this._index.Declarations[id]) {
@ -118,8 +117,9 @@ export class EditorContextListener extends Plugin {
async nodesAtEditorPosition(position: any) { async nodesAtEditorPosition(position: any) {
const lastCompilationResult = await this.call('compilerArtefacts', 'getLastCompilationResult') 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) { 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 nodes
} }
return [] 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) { async definitionAtPosition(position: any) {
const nodes = await this.nodesAtEditorPosition(position) const nodes = await this.nodesAtEditorPosition(position)
console.log(nodes) console.log(nodes)
console.log(this._index.FlatReferences) console.log(this._index.FlatReferences)
let nodeDefinition: AstNode let nodeDefinition: any
let node: AstNode let node: any
if (nodes && nodes.length) { if (nodes && nodes.length) {
node = nodes[nodes.length - 1] node = nodes[nodes.length - 1]
nodeDefinition = node nodeDefinition = node
@ -165,23 +169,33 @@ export class EditorContextListener extends Plugin {
} }
async jumpToDefinition(position: any) { async positionOfDefinition(node: any) {
const node = await this.definitionAtPosition(position)
if (node) { if (node) {
if (node.src) { if (node.src) {
const position = sourceMappingDecoder.decode(node.src) const position = sourceMappingDecoder.decode(node.src)
if (position) { 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 * onClick jump to position of ast node in the editor
*/ */
async jumpToPosition(position: any) { async jumpToPosition(position: any) {
const jumpToLine = async (fileName: string, lineColumn: any) => { const jumpToLine = async (fileName: string, lineColumn: any) => {
if (fileName !== await this.call('fileManager', 'file')) { if (fileName !== await this.call('fileManager', 'file')) {
console.log('jump to file', fileName)
await this.call('fileManager', 'open', fileName) await this.call('fileManager', 'open', fileName)
} }
if (lineColumn.start && lineColumn.start.line >= 0 && lineColumn.start.column >= 0) { 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') const lastCompilationResult = await this.call('compilerArtefacts', 'getLastCompilationResult')
console.log(lastCompilationResult.getSourceCode().sources)
console.log(position)
if (lastCompilationResult && lastCompilationResult.languageversion.indexOf('soljson') === 0 && lastCompilationResult.data) { if (lastCompilationResult && lastCompilationResult.languageversion.indexOf('soljson') === 0 && lastCompilationResult.data) {
const lineColumn = await this.call('offsetToLineColumnConverter', 'offsetToLineColumn', const lineColumn = await this.call('offsetToLineColumnConverter', 'offsetToLineColumn',
position, position,
@ -197,6 +213,7 @@ export class EditorContextListener extends Plugin {
lastCompilationResult.getAsts()) lastCompilationResult.getAsts())
const filename = lastCompilationResult.getSourceName(position.file) const filename = lastCompilationResult.getSourceName(position.file)
// TODO: refactor with rendererAPI.errorClick // TODO: refactor with rendererAPI.errorClick
console.log(filename, lineColumn)
jumpToLine(filename, lineColumn) jumpToLine(filename, lineColumn)
} }
} }
@ -206,10 +223,10 @@ export class EditorContextListener extends Plugin {
this._stopHighlighting() this._stopHighlighting()
this.currentPosition = cursorPosition this.currentPosition = cursorPosition
this.currentFile = file this.currentFile = file
if (compilationResult && compilationResult.data && compilationResult.data.sources[file]) { let urlFromPath = await this.call('fileManager', 'getUrlFromPath', this.currentFile)
const nodes = sourceMappingDecoder.nodesAtPosition(null, cursorPosition, compilationResult.data.sources[file]) 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 this.nodes = nodes
console.log(nodes)
if (nodes && nodes.length && nodes[nodes.length - 1]) { if (nodes && nodes.length && nodes[nodes.length - 1]) {
await this._highlightExpressions(nodes[nodes.length - 1], compilationResult) 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 monaco from '../types/monaco'
import { IPosition, languages } from 'monaco-editor' import { IPosition, languages } from 'monaco-editor'
import { sourceMappingDecoder } from '@remix-project/remix-debug' import { sourceMappingDecoder } from '@remix-project/remix-debug'
import { RemixHoverProvider } from './providers/hoverProvider'
type cursorPosition = { type cursorPosition = {
startLineNumber: number, startLineNumber: number,
@ -435,6 +436,7 @@ export const EditorUI = (props: EditorUIProps) => {
monacoRef.current.languages.registerReferenceProvider('remix-solidity', { monacoRef.current.languages.registerReferenceProvider('remix-solidity', {
async provideReferences(model: monaco.editor.ITextModel, position: monaco.Position, context: any, token: monaco.CancellationToken) { async provideReferences(model: monaco.editor.ITextModel, position: monaco.Position, context: any, token: monaco.CancellationToken) {
const cursorPosition = props.editorAPI.getCursorPosition() const cursorPosition = props.editorAPI.getCursorPosition()
const nodes = await props.plugin.call('contextualListener', 'referrencesAtPosition', cursorPosition) const nodes = await props.plugin.call('contextualListener', 'referrencesAtPosition', cursorPosition)
const references = [] const references = []
@ -471,91 +473,138 @@ export const EditorUI = (props: EditorUIProps) => {
} }
}) })
monacoRef.current.languages.registerHoverProvider('remix-solidity', { monacoRef.current.languages.registerHoverProvider('remix-solidity', {
provideHover: async function (model: any, position: monaco.Position) { provideHover: async function (model: any, position: monaco.Position) {
console.log('--------------------')
const cursorPosition = props.editorAPI.getHoverPosition(position) const cursorPosition = props.editorAPI.getHoverPosition(position)
const nodeDefinition = await props.plugin.call('contextualListener', 'definitionAtPosition', cursorPosition) const nodeDefinition = await props.plugin.call('contextualListener', 'definitionAtPosition', cursorPosition)
console.log(nodeDefinition) console.log(nodeDefinition)
const contents = [] const contents = []
const getDocs = async (node: any) => { const getDocs = async (node: any) => {
if(node.documentation && node.documentation.text) { if (node.documentation && node.documentation.text) {
let text = '' let text = ''
node.documentation.text.split('\n').forEach(line => { node.documentation.text.split('\n').forEach(line => {
text += `${line.trim()}\n` 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({ contents.push({
value: text value: `${filename} ${lineColumn.start.line}:${lineColumn.start.column}`
}) })
}
} }
const getVariableDeclaration = async (node: any) => { const getVariableDeclaration = async (node: any) => {
if(node.typeDescriptions && node.typeDescriptions.typeString) { if (node.typeDescriptions && node.typeDescriptions.typeString) {
return `${node.typeDescriptions.typeString} ${node.name}` 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) => { const getlinearizedBaseContracts = async (node: any) => {
if(node.parameters && node.parameters.parameters) {
let params = [] let params = []
for(const param of node.parameters.parameters) { for (const id of node.linearizedBaseContracts) {
params.push(await getVariableDeclaration(param)) 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) return null
if (nodeDefinition.absolutePath) { if (nodeDefinition.absolutePath) {
const target = await props.plugin.call('fileManager', 'getPathFromUrl', nodeDefinition.absolutePath) const target = await props.plugin.call('fileManager', 'getPathFromUrl', nodeDefinition.absolutePath)
if (target.file !== nodeDefinition.absolutePath) { if (target.file !== nodeDefinition.absolutePath) {
contents.push({
value: `${target.file}`
})
}
contents.push({ contents.push({
value: `${target.file}` value: `${nodeDefinition.absolutePath}`
}) })
}
contents.push({
value: `${nodeDefinition.absolutePath}`
})
} }
if (nodeDefinition.typeDescriptions && nodeDefinition.nodeType === 'VariableDeclaration') { if (nodeDefinition.typeDescriptions && nodeDefinition.nodeType === 'VariableDeclaration') {
contents.push({ contents.push({
value: await getVariableDeclaration(nodeDefinition) value: await getVariableDeclaration(nodeDefinition)
}) })
} }
else if (nodeDefinition.typeDescriptions && nodeDefinition.nodeType === 'ElementaryTypeName') { else if (nodeDefinition.typeDescriptions && nodeDefinition.nodeType === 'ElementaryTypeName') {
contents.push({ contents.push({
value: `${nodeDefinition.typeDescriptions.typeString}` value: `${nodeDefinition.typeDescriptions.typeString}`
}) })
} else if (nodeDefinition.nodeType === 'FunctionDefinition') { } else if (nodeDefinition.nodeType === 'FunctionDefinition') {
contents.push({ contents.push({
value: `(${nodeDefinition.visibility} function) ${nodeDefinition.name}: ${await getParamaters(nodeDefinition)}` value: `function ${nodeDefinition.name} ${await getParamaters(nodeDefinition.parameters)} ${nodeDefinition.visibility} ${nodeDefinition.stateMutability}${await getOverrides(nodeDefinition)} returns ${await getParamaters(nodeDefinition.returnParameters)}`
}) })
getDocs(nodeDefinition)
getDocs(nodeDefinition)
} else if (nodeDefinition.nodeType === 'ContractDefinition') {
contents.push({
value: `${nodeDefinition.contractKind} ${nodeDefinition.name} ${await getlinearizedBaseContracts(nodeDefinition)}`
})
getDocs(nodeDefinition)
} else { } else {
contents.push({ contents.push({
value: `${nodeDefinition.nodeType}` value: `${nodeDefinition.nodeType}`
}) })
getDocs(nodeDefinition) getDocs(nodeDefinition)
} }
getLinks(nodeDefinition)
for (const key in contents) { 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 { return {
range: new monaco.Range( range: new monaco.Range(
position.lineNumber, position.lineNumber,
position.column, position.column,
position.lineNumber, position.lineNumber,
model.getLineMaxColumn(position.lineNumber) model.getLineMaxColumn(position.lineNumber)
), ),
contents: contents contents: contents
}; };
} }
}) })
loadTypes(monacoRef.current) loadTypes(monacoRef.current)

Loading…
Cancel
Save