diff --git a/apps/remix-ide-e2e/src/tests/editor_line_text.test.ts b/apps/remix-ide-e2e/src/tests/editor_line_text.test.ts new file mode 100644 index 0000000000..afff1c8f28 --- /dev/null +++ b/apps/remix-ide-e2e/src/tests/editor_line_text.test.ts @@ -0,0 +1,69 @@ +'use strict' + +import { NightwatchBrowser } from 'nightwatch' +import init from '../helpers/init' + +module.exports = { + + before: function (browser: NightwatchBrowser, done: VoidFunction) { + init(browser, done, 'http://127.0.0.1:8080', true) + }, + 'Should add line texts': function (browser: NightwatchBrowser) { + browser + .openFile('contracts') + .openFile('contracts/1_Storage.sol') + .addFile('scripts/addlinetext.ts', {content: addLineText}) + .pause(4000) + .executeScriptInTerminal('remix.exeCurrent()') + .pause(4000) + .openFile('contracts/1_Storage.sol') + .useXpath() + .waitForElementVisible("//*[@class='view-line' and contains(.,'contract')]//span//span[contains(.,'mylinetext1')]") + .waitForElementVisible("//*[@class='view-line' and contains(.,'function')]//span//span[contains(.,'mylinetext2')]") + } +} + +const addLineText = ` +(async () => { + + await remix.call('editor', 'discardLineTexts' as any) + let linetext = { + content: 'mylinetext1', + position: { + start: { + line: 9, + column: 1, + } + }, + hide: false, + className: 'text-muted small', + afterContentClassName: 'text-muted small fas fa-gas-pump pl-4', + hoverMessage: [{ + value: 'hovering1', + }, + ], + } + + await remix.call('editor', 'addLineText' as any, linetext, 'contracts/1_Storage.sol') + + + linetext = { + content: 'mylinetext2', + position: { + start: { + line: 17, + column: 1, + } + }, + hide: false, + className: 'text-muted small', + afterContentClassName: 'text-muted small fas fa-gas-pump pl-4', + hoverMessage: [{ + value: 'hovering2', + }, + ], + } + + await remix.call('editor', 'addLineText' as any, linetext, 'contracts/1_Storage.sol') + +})()` \ No newline at end of file diff --git a/apps/remix-ide/src/app/editor/editor.js b/apps/remix-ide/src/app/editor/editor.js index 97dc63f988..34b9c172e5 100644 --- a/apps/remix-ide/src/app/editor/editor.js +++ b/apps/remix-ide/src/app/editor/editor.js @@ -13,7 +13,7 @@ const profile = { name: 'editor', description: 'service - editor', version: packageJson.version, - methods: ['highlight', 'discardHighlight', 'clearAnnotations', 'addAnnotation', 'gotoLine', 'revealRange', 'getCursorPosition'] + methods: ['highlight', 'discardHighlight', 'clearAnnotations', 'addLineText', 'discardLineTexts', 'addAnnotation', 'gotoLine', 'revealRange', 'getCursorPosition'] } class Editor extends Plugin { @@ -26,8 +26,8 @@ class Editor extends Plugin { remixDark: 'remix-dark' } - this.registeredDecorations = { sourceAnnotationsPerFile: {}, markerPerFile: {} } - this.currentDecorations = { sourceAnnotationsPerFile: {}, markerPerFile: {} } + this.registeredDecorations = { sourceAnnotationsPerFile: {}, markerPerFile: {}, lineTextPerFile: {} } + this.currentDecorations = { sourceAnnotationsPerFile: {}, markerPerFile: {}, lineTextPerFile: {} } // Init this.event = new EventManager() @@ -567,6 +567,18 @@ class Editor extends Plugin { this.clearDecorationsByPlugin(session, from, 'markerPerFile', this.registeredDecorations, this.currentDecorations) } } + + async addLineText (lineText, filePath) { + filePath = filePath || this.currentFile + await this.addDecoration(lineText, filePath, 'lineTextPerFile') + } + + discardLineTexts() { + const { from } = this.currentRequest + for (const session in this.sessions) { + this.clearDecorationsByPlugin(session, from, 'lineTextPerFile', this.registeredDecorations, this.currentDecorations) + } + } } module.exports = Editor diff --git a/libs/remix-ui/editor/src/lib/remix-ui-editor.tsx b/libs/remix-ui/editor/src/lib/remix-ui-editor.tsx index 30e8323faf..aad442eef4 100644 --- a/libs/remix-ui/editor/src/lib/remix-ui-editor.tsx +++ b/libs/remix-ui/editor/src/lib/remix-ui-editor.tsx @@ -5,6 +5,7 @@ import { reducerActions, reducerListener, initialState } from './actions/editor' import { solidityTokensProvider, solidityLanguageConfig } from './syntaxes/solidity' import { cairoTokensProvider, cairoLanguageConfig } from './syntaxes/cairo' import { zokratesTokensProvider, zokratesLanguageConfig } from './syntaxes/zokrates' +import { IMarkdownString } from 'monaco-editor' import './remix-ui-editor.css' import { loadTypes } from './web-types' @@ -40,6 +41,25 @@ type sourceMarker = { hide: boolean } +export type lineText = { + position: { + start: { + line: number + column: number + }, + end: { + line: number + column: number + } + }, + from?: string // plugin name + content: string + className: string + afterContentClassName: string + hide: boolean, + hoverMessage: IMarkdownString | IMarkdownString[] +} + loader.config({ paths: { vs: 'assets/js/monaco-editor/dev/vs' } }) export type DecorationsReturn = { @@ -261,7 +281,7 @@ export const EditorUI = (props: EditorUIProps) => { } }, [props.currentFile]) - const convertToMonacoDecoration = (decoration: sourceAnnotation | sourceMarker, typeOfDecoration: string) => { + const convertToMonacoDecoration = (decoration: lineText | sourceAnnotation | sourceMarker, typeOfDecoration: string) => { if (typeOfDecoration === 'sourceAnnotationsPerFile') { decoration = decoration as sourceAnnotation return { @@ -291,6 +311,19 @@ export const EditorUI = (props: EditorUIProps) => { } } } + if (typeOfDecoration === 'lineTextPerFile') { + const lineTextDecoration = decoration as lineText + return { + type: typeOfDecoration, + range: new monacoRef.current.Range(lineTextDecoration.position.start.line + 1, lineTextDecoration.position.start.column + 1, lineTextDecoration.position.start.line + 1, 1024), + options: { + after: { content: ` ${lineTextDecoration.content}`, inlineClassName: `${lineTextDecoration.className}` }, + afterContentClassName: `${lineTextDecoration.afterContentClassName}`, + hoverMessage : lineTextDecoration.hoverMessage + }, + + } + } } props.editorAPI.clearDecorationsByPlugin = (filePath: string, plugin: string, typeOfDecoration: string, registeredDecorations: any, currentDecorations: any) => {