jumpToDefinition

editorcontextDummy
bunsenstraat 3 years ago
parent ed8a8732f1
commit 9ff7f930d9
  1. 100
      libs/remix-core-plugin/src/lib/editor-context-listener.ts
  2. 40
      libs/remix-ui/editor-context-view/src/lib/remix-ui-editor-context-view.tsx
  3. 78
      libs/remix-ui/editor/src/lib/remix-ui-editor.tsx
  4. 64134
      package-lock.json

@ -2,14 +2,24 @@
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: ['referencesOf', 'getActiveHighlights', 'gasEstimation', 'declarationOf'],
methods: ['jumpToDefinition', 'nodesAtEditorPosition', 'referencesOf', 'getActiveHighlights', 'gasEstimation', 'declarationOf', 'jumpTo'],
events: [],
version: '0.0.1'
}
export function isDefinition(node: any) {
return node.nodeType === 'ContractDefinition' ||
node.nodeType === 'FunctionDefinition' ||
node.nodeType === 'ModifierDefinition' ||
node.nodeType === 'VariableDeclaration' ||
node.nodeType === 'StructDefinition' ||
node.nodeType === 'EventDefinition'
}
/*
trigger contextChanged(nodes)
*/
@ -27,7 +37,7 @@ export class EditorContextListener extends Plugin {
contract: any
activated: boolean
constructor (astWalker) {
constructor(astWalker) {
super(profile)
this.activated = false
this._index = {
@ -39,7 +49,7 @@ export class EditorContextListener extends Plugin {
this.astWalker = astWalker
}
onActivation () {
onActivation() {
this.on('editor', 'contentChanged', () => { this._stopHighlighting() })
this.on('solidity', 'compilationFinished', (file, source, languageVersion, data, input, version) => {
@ -50,6 +60,7 @@ export class EditorContextListener extends Plugin {
FlatReferences: {}
}
this._buildIndex(data, source)
console.log(this._index)
})
setInterval(async () => {
@ -72,22 +83,78 @@ export class EditorContextListener extends Plugin {
this.activated = true
}
getActiveHighlights () {
getActiveHighlights() {
return [...this._activeHighlights]
}
declarationOf (node) {
declarationOf(node) {
if (node && node.referencedDeclaration) {
return this._index.FlatReferences[node.referencedDeclaration]
}
return null
}
referencesOf (node) {
referencesOf(node) {
return this._index.Declarations[node.id]
}
async _highlightItems (cursorPosition, compilationResult, file) {
async nodesAtEditorPosition(position: any){
const lastCompilationResult = await this.call('compilerArtefacts', 'getLastCompilationResult')
if (lastCompilationResult && lastCompilationResult.languageversion.indexOf('soljson') === 0 && lastCompilationResult.data) {
const nodes = sourceMappingDecoder.nodesAtPosition(null, position, lastCompilationResult.data.sources[this.currentFile])
return nodes
}
return []
}
async jumpToDefinition(position: any) {
const nodes = await this.nodesAtEditorPosition(position)
console.log(nodes)
let nodeDeclaration: AstNode
let node: AstNode
if (nodes && nodes.length) {
node = nodes[nodes.length - 1]
if (!isDefinition(node)) {
nodeDeclaration = await this.declarationOf(node)
} else {
nodeDeclaration = node
}
}
console.log(node, nodeDeclaration)
if (nodeDeclaration && nodeDeclaration.src) {
console.log(nodeDeclaration)
const position = sourceMappingDecoder.decode(nodeDeclaration.src)
if (position) {
await this.jumpToPosition(position)
}
}
}
/*
* 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')) {
await this.call('fileManager', 'open', fileName)
}
if (lineColumn.start && lineColumn.start.line >= 0 && lineColumn.start.column >= 0) {
this.call('editor', 'gotoLine', lineColumn.start.line, lineColumn.end.column + 1)
}
}
const lastCompilationResult = await this.call('compilerArtefacts', 'getLastCompilationResult')
if (lastCompilationResult && lastCompilationResult.languageversion.indexOf('soljson') === 0 && lastCompilationResult.data) {
const lineColumn = await this.call('offsetToLineColumnConverter', 'offsetToLineColumn',
position,
position.file,
lastCompilationResult.getSourceCode().sources,
lastCompilationResult.getAsts())
const filename = lastCompilationResult.getSourceName(position.file)
// TODO: refactor with rendererAPI.errorClick
jumpToLine(filename, lineColumn)
}
}
async _highlightItems(cursorPosition, compilationResult, file) {
if (this.currentPosition === cursorPosition) return
this._stopHighlighting()
this.currentPosition = cursorPosition
@ -95,6 +162,7 @@ export class EditorContextListener extends Plugin {
if (compilationResult && compilationResult.data && compilationResult.data.sources[file]) {
const nodes = sourceMappingDecoder.nodesAtPosition(null, cursorPosition, compilationResult.data.sources[file])
this.nodes = nodes
console.log(nodes)
if (nodes && nodes.length && nodes[nodes.length - 1]) {
await this._highlightExpressions(nodes[nodes.length - 1], compilationResult)
}
@ -102,7 +170,7 @@ export class EditorContextListener extends Plugin {
}
}
_buildIndex (compilationResult, source) {
_buildIndex(compilationResult, source) {
if (compilationResult && compilationResult.sources) {
const callback = (node) => {
if (node && node.referencedDeclaration) {
@ -119,7 +187,7 @@ export class EditorContextListener extends Plugin {
}
}
async _highlight (node, compilationResult) {
async _highlight(node, compilationResult) {
if (!node) return
const position = sourceMappingDecoder.decode(node.src)
const fileTarget = compilationResult.getSourceName(position.file)
@ -132,7 +200,7 @@ export class EditorContextListener extends Plugin {
}
}
async _highlightInternal (position, node, compilationResult) {
async _highlightInternal(position, node, compilationResult) {
if (node.nodeType === 'Block') return
if (compilationResult && compilationResult.languageversion.indexOf('soljson') === 0) {
let lineColumn = await this.call('offsetToLineColumnConverter', 'offsetToLineColumn', position, position.file, compilationResult.getSourceCode().sources, compilationResult.getAsts())
@ -157,7 +225,7 @@ export class EditorContextListener extends Plugin {
return null
}
async _highlightExpressions (node, compilationResult) {
async _highlightExpressions(node, compilationResult) {
const highlights = async (id) => {
if (this._index.Declarations && this._index.Declarations[id]) {
const refs = this._index.Declarations[id]
@ -178,13 +246,13 @@ export class EditorContextListener extends Plugin {
this.results = compilationResult
}
_stopHighlighting () {
_stopHighlighting() {
this.call('editor', 'discardHighlight')
this.emit('stopHighlighting')
this._activeHighlights = []
}
gasEstimation (node) {
gasEstimation(node) {
this._loadContractInfos(node)
let executionCost, codeDepositCost
if (node.nodeType === 'FunctionDefinition') {
@ -207,7 +275,7 @@ export class EditorContextListener extends Plugin {
return { executionCost, codeDepositCost }
}
_loadContractInfos (node) {
_loadContractInfos(node) {
const path = (this.nodes.length && this.nodes[0].absolutePath) || this.results.source.target
for (const i in this.nodes) {
if (this.nodes[i].id === node.scope) {
@ -222,7 +290,9 @@ export class EditorContextListener extends Plugin {
}
}
_getInputParams (node) {
_getInputParams(node) {
const params = []
const target = node.parameters
// for (const i in node.children) {

@ -35,14 +35,9 @@ export type gasEstimationType = {
}
export interface RemixUiEditorContextViewProps {
hide: boolean,
gotoLine: (line: number, column: number) => void,
openFile: (fileName: string) => void,
getLastCompilationResult: () => any,
offsetToLineColumn: (position: any, file: any, sources: any, asts: any) => any,
getCurrentFileName: () => string
jumpToPosition: (position: any) => void
onContextListenerChanged: (listener: onContextListenerChangedListener) => void
onCurrentFileChanged: (listener: ononCurrentFileChangedListener) => void
referencesOf: (nodes: astNode) => Array<astNode>
getActiveHighlights: () => Array<astNodeLight>
gasEstimation: (node: astNode) => gasEstimationType
declarationOf: (node: astNode) => astNode
@ -91,6 +86,7 @@ export function RemixUiEditorContextView (props: RemixUiEditorContextViewProps)
nextNodeDeclaration = nextNode
}
}
console.log(nextNode, nextNodeDeclaration)
if (nextNodeDeclaration && currentNodeDeclaration.current && nextNodeDeclaration.id === currentNodeDeclaration.current.id) return
currentNodeDeclaration.current = nextNodeDeclaration
@ -134,35 +130,13 @@ export function RemixUiEditorContextView (props: RemixUiEditorContextViewProps)
}
}
/*
* onClick jump to ast node in the editor
*/
const _jumpToInternal = async (position: any) => {
const jumpToLine = async (fileName: string, lineColumn: any) => {
if (fileName !== await props.getCurrentFileName()) {
await props.openFile(fileName)
}
if (lineColumn.start && lineColumn.start.line >= 0 && lineColumn.start.column >= 0) {
props.gotoLine(lineColumn.start.line, lineColumn.end.column + 1)
}
}
const lastCompilationResult = await props.getLastCompilationResult()
if (lastCompilationResult && lastCompilationResult.languageversion.indexOf('soljson') === 0 && lastCompilationResult.data) {
const lineColumn = await props.offsetToLineColumn(
position,
position.file,
lastCompilationResult.getSourceCode().sources,
lastCompilationResult.getAsts())
const filename = lastCompilationResult.getSourceName(position.file)
// TODO: refactor with rendererAPI.errorClick
jumpToLine(filename, lineColumn)
}
}
const _render = () => {
const node = currentNodeDeclaration.current
if (!node) return (<div></div>)
const references = state.activeHighlights
console.log(node)
const type = node.typeDescriptions && node.typeDescriptions.typeString ? node.typeDescriptions.typeString : node.nodeType
const referencesCount = `${references ? references.length : '0'} reference(s)`
@ -170,9 +144,10 @@ export function RemixUiEditorContextView (props: RemixUiEditorContextViewProps)
const jumpTo = () => {
if (node && node.src) {
console.log(node)
const position = sourceMappingDecoder.decode(node.src)
if (position) {
_jumpToInternal(position)
props.jumpToPosition(position)
}
}
}
@ -182,7 +157,8 @@ export function RemixUiEditorContextView (props: RemixUiEditorContextViewProps)
e.target.dataset.action === 'next' ? loopOverReferences.current++ : loopOverReferences.current--
if (loopOverReferences.current < 0) loopOverReferences.current = nodes.length - 1
if (loopOverReferences.current >= nodes.length) loopOverReferences.current = 0
_jumpToInternal(nodes[loopOverReferences.current].position)
console.log(loopOverReferences.current)
props.jumpToPosition(nodes[loopOverReferences.current].position)
}
return (

@ -1,12 +1,15 @@
import React, { useState, useRef, useEffect, useReducer } from 'react' // eslint-disable-line
import { RemixUiEditorContextView, astNode } from '@remix-ui/editor-context-view'
import Editor, { loader } from '@monaco-editor/react'
import Editor, { loader, Monaco } from '@monaco-editor/react'
import { reducerActions, reducerListener, initialState } from './actions/editor'
import { language, conf } from './syntax'
import { cairoLang, cairoConf } from './cairoSyntax'
import './remix-ui-editor.css'
import { loadTypes } from './web-types'
import monaco from '../types/monaco'
import { IPosition, languages } from 'monaco-editor'
import { sourceMappingDecoder } from '@remix-project/remix-debug'
type cursorPosition = {
startLineNumber: number,
@ -67,6 +70,7 @@ export interface EditorUIProps {
getFontSize: () => number,
getValue: (uri: string) => string
getCursorPosition: () => cursorPosition
getHoverPosition: (position: IPosition) => number
addDecoration: (marker: sourceMarker, filePath: string, typeOfDecoration: string) => DecorationsReturn
clearDecorationsByPlugin: (filePath: string, plugin: string, typeOfDecoration: string, registeredDecorations: any, currentDecorations: any) => DecorationsReturn
keepDecorationsFor: (filePath: string, plugin: string, typeOfDecoration: string, registeredDecorations: any, currentDecorations: any) => DecorationsReturn
@ -80,7 +84,7 @@ export const EditorUI = (props: EditorUIProps) => {
const currentFileRef = useRef('')
// const currentDecorations = useRef({ sourceAnnotationsPerFile: {}, markerPerFile: {} }) // decorations that are currently in use by the editor
// const registeredDecorations = useRef({}) // registered decorations
const [editorModelsState, dispatch] = useReducer(reducerActions, initialState)
const formatColor = (name) => {
@ -237,7 +241,7 @@ export const EditorUI = (props: EditorUIProps) => {
monacoRef.current.editor.setModelLanguage(file.model, 'remix-solidity')
} else if (file.language === 'cairo') {
monacoRef.current.editor.setModelLanguage(file.model, 'remix-cairo')
}
}
}, [props.currentFile])
const convertToMonacoDecoration = (decoration: sourceAnnotation | sourceMarker, typeOfDecoration: string) => {
@ -293,7 +297,7 @@ export const EditorUI = (props: EditorUIProps) => {
registeredDecorations: newRegisteredDecorations
}
}
props.editorAPI.keepDecorationsFor = (filePath: string, plugin: string, typeOfDecoration: string, registeredDecorations: any, currentDecorations: any) => {
const model = editorModelsState[filePath]?.model
if (!model) return {
@ -322,7 +326,7 @@ export const EditorUI = (props: EditorUIProps) => {
registeredDecorations: [{ value: decoration, type: typeOfDecoration }]
}
}
props.editorAPI.addDecoration = (marker: sourceMarker, filePath: string, typeOfDecoration: string) => {
return addDecoration(marker, filePath, typeOfDecoration)
}
@ -349,6 +353,16 @@ export const EditorUI = (props: EditorUIProps) => {
}
}
props.editorAPI.getHoverPosition = (position: monaco.Position) => {
if (!monacoRef.current) return
const model = editorModelsState[currentFileRef.current]?.model
if (model) {
return model.getOffsetAt(position)
}else{
return 0
}
}
props.editorAPI.getFontSize = () => {
if (!editorRef.current) return
return editorRef.current.getOption(43).fontSize
@ -381,7 +395,7 @@ export const EditorUI = (props: EditorUIProps) => {
}
}
function handleEditorDidMount (editor) {
function handleEditorDidMount(editor) {
editorRef.current = editor
defineAndSetTheme(monacoRef.current)
reducerListener(props.plugin, dispatch, monacoRef.current, editorRef.current, props.events)
@ -399,7 +413,7 @@ export const EditorUI = (props: EditorUIProps) => {
})
}
function handleEditorWillMount (monaco) {
function handleEditorWillMount(monaco) {
monacoRef.current = monaco
// Register a new language
monacoRef.current.languages.register({ id: 'remix-solidity' })
@ -410,6 +424,49 @@ export const EditorUI = (props: EditorUIProps) => {
monacoRef.current.languages.setMonarchTokensProvider('remix-cairo', cairoLang)
monacoRef.current.languages.setLanguageConfiguration('remix-cairo', cairoConf)
// register Definition Provider
monacoRef.current.languages.registerDefinitionProvider('remix-solidity', {
provideDefinition(model: monaco.editor.ITextModel, position: monaco.Position, token: monaco.CancellationToken){
const cursorPosition = props.editorAPI.getCursorPosition()
props.plugin.call('contextualListener', 'jumpToDefinition', cursorPosition)
return null
}
})
monacoRef.current.languages.registerHoverProvider('remix-solidity', {
provideHover: async function (model: any, position: monaco.Position) {
//console.log(position)
const cursorPosition = props.editorAPI.getHoverPosition(position)
//console.log(cursorPosition)
const compilationResult = await props.plugin.call('compilerArtefacts', 'getLastCompilationResult')
const file = await props.plugin.call('fileManager', 'file')
if (compilationResult && compilationResult.data && compilationResult.data.sources[file]) {
const nodes = sourceMappingDecoder.nodesAtPosition(null, cursorPosition, compilationResult.data.sources[file])
// console.log(cursorPosition, nodes)
// loop over nodes
if (nodes && nodes.length) {
nodes.forEach((node) => {
const position = sourceMappingDecoder.decode(node.src)
const fileTarget = compilationResult.getSourceName(position.file)
// console.log(position, fileTarget)
})
}
}
return {
range: new monaco.Range(
position.lineNumber,
position.column,
position.lineNumber,
model.getLineMaxColumn(position.lineNumber)
),
contents: [
{ value: '<div>test html</div>' }
]
};
}
})
loadTypes(monacoRef.current)
}
@ -427,14 +484,9 @@ export const EditorUI = (props: EditorUIProps) => {
<div className="contextview">
<RemixUiEditorContextView
hide={false}
gotoLine={(line, column) => props.plugin.call('editor', 'gotoLine', line, column)}
openFile={(file) => props.plugin.call('fileManager', 'switchFile', file)}
getLastCompilationResult={() => { return props.plugin.call('compilerArtefacts', 'getLastCompilationResult') } }
offsetToLineColumn={(position, file, sources, asts) => { return props.plugin.call('offsetToLineColumnConverter', 'offsetToLineColumn', position, file, sources, asts) } }
getCurrentFileName={() => { return props.plugin.call('fileManager', 'file') } }
jumpToPosition={(position) => props.plugin.call('contextualListener', 'jumpToPosition', position)}
onContextListenerChanged={(listener) => { props.plugin.on('contextualListener', 'contextChanged', listener) }}
onCurrentFileChanged={(listener) => { props.plugin.on('fileManager', 'currentFileChanged', listener) }}
referencesOf={(node: astNode) => { return props.plugin.call('contextualListener', 'referencesOf', node) }}
getActiveHighlights={() => { return props.plugin.call('contextualListener', 'getActiveHighlights') }}
gasEstimation={(node: astNode) => { return props.plugin.call('contextualListener', 'gasEstimation', node) }}
declarationOf={(node: astNode) => { return props.plugin.call('contextualListener', 'declarationOf', node) }}

64134
package-lock.json generated

File diff suppressed because it is too large Load Diff
Loading…
Cancel
Save