From 82edba9b84770449eefb3ed6283009cecd34b3e3 Mon Sep 17 00:00:00 2001 From: filip mertens Date: Thu, 21 Jul 2022 15:39:04 +0200 Subject: [PATCH 01/10] add test --- apps/remix-ide/src/app/panels/tab-proxy.js | 6 ++ .../src/app/plugins/file-decorator.ts | 60 +++++++++++ libs/remix-ui/file-decorators/.babelrc | 4 + libs/remix-ui/file-decorators/.eslintrc | 19 ++++ libs/remix-ui/file-decorators/src/index.ts | 3 + .../lib/components/file-decoration-icon.tsx | 51 ++++++++++ .../file-decoration-custom-icon.tsx | 13 +++ .../file-decoration-error-icon.tsx | 14 +++ .../file-decoration-tooltip.tsx | 33 +++++++ .../file-decoration-warning-icon.tsx | 11 +++ .../file-decorators/src/lib/helper/index.tsx | 11 +++ .../file-decorators/src/lib/types/index.ts | 29 ++++++ libs/remix-ui/file-decorators/tsconfig.json | 16 +++ .../file-decorators/tsconfig.lib.json | 13 +++ libs/remix-ui/tabs/src/lib/remix-ui-tabs.tsx | 99 +++++++++++++++---- libs/remix-ui/workspace/src/index.ts | 1 + .../workspace/src/lib/actions/events.ts | 12 ++- .../workspace/src/lib/actions/payload.ts | 7 ++ .../src/lib/components/file-explorer.tsx | 3 +- .../src/lib/components/file-label.tsx | 21 +++- .../src/lib/components/file-render.tsx | 18 +++- .../workspace/src/lib/reducers/workspace.ts | 31 ++++-- .../workspace/src/lib/remix-ui-workspace.tsx | 2 + .../remix-ui/workspace/src/lib/types/index.ts | 2 + 24 files changed, 444 insertions(+), 35 deletions(-) create mode 100644 apps/remix-ide/src/app/plugins/file-decorator.ts create mode 100644 libs/remix-ui/file-decorators/.babelrc create mode 100644 libs/remix-ui/file-decorators/.eslintrc create mode 100644 libs/remix-ui/file-decorators/src/index.ts create mode 100644 libs/remix-ui/file-decorators/src/lib/components/file-decoration-icon.tsx create mode 100644 libs/remix-ui/file-decorators/src/lib/components/filedecorationicons/file-decoration-custom-icon.tsx create mode 100644 libs/remix-ui/file-decorators/src/lib/components/filedecorationicons/file-decoration-error-icon.tsx create mode 100644 libs/remix-ui/file-decorators/src/lib/components/filedecorationicons/file-decoration-tooltip.tsx create mode 100644 libs/remix-ui/file-decorators/src/lib/components/filedecorationicons/file-decoration-warning-icon.tsx create mode 100644 libs/remix-ui/file-decorators/src/lib/helper/index.tsx create mode 100644 libs/remix-ui/file-decorators/src/lib/types/index.ts create mode 100644 libs/remix-ui/file-decorators/tsconfig.json create mode 100644 libs/remix-ui/file-decorators/tsconfig.lib.json diff --git a/apps/remix-ide/src/app/panels/tab-proxy.js b/apps/remix-ide/src/app/panels/tab-proxy.js index 82758c169f..3821788e18 100644 --- a/apps/remix-ide/src/app/panels/tab-proxy.js +++ b/apps/remix-ide/src/app/panels/tab-proxy.js @@ -169,6 +169,10 @@ export class TabProxy extends Plugin { this.on('manager', 'pluginDeactivated', (profile) => { this.removeTab(profile.name) }) + + this.on('fileDecorator', 'fileDecoratorsChanged', async (items) => { + this.tabsApi.setFileDecorations(items) + }) try { this.themeQuality = (await this.call('theme', 'currentTheme') ).quality @@ -306,6 +310,7 @@ export class TabProxy extends Plugin { updateComponent(state) { return { this.tabsApi = api } this.dispatch({ + plugin: this, loadedTabs: this.loadedTabs, onSelect, onClose, diff --git a/apps/remix-ide/src/app/plugins/file-decorator.ts b/apps/remix-ide/src/app/plugins/file-decorator.ts new file mode 100644 index 0000000000..60a14ef47f --- /dev/null +++ b/apps/remix-ide/src/app/plugins/file-decorator.ts @@ -0,0 +1,60 @@ +'use strict' + +import { default as deepequal } from 'deep-equal' // eslint-disable-line + +import { Plugin } from '@remixproject/engine' +import { fileDecoration } from '@remix-ui/file-decorators' + +const profile = { + name: 'fileDecorator', + desciption: 'Keeps decorators of the files', + methods: ['setFileDecorators', 'clearFileDecorators'], + events: ['fileDecoratorsChanged'], + version: '0.0.1' +} + +export class FileDecorator extends Plugin { + private _fileStates: fileDecoration[] = [] + constructor() { + super(profile) + } + /** + * + * @param fileStates Array of file states + */ + async setFileDecorators(fileStates: fileDecoration[] | fileDecoration) { + const workspace = await this.call('filePanel', 'getCurrentWorkspace') + function sortByPath( a: fileDecoration, b: fileDecoration ) { + if ( a.path < b.path ){ + return -1; + } + if ( a.path > b.path ){ + return 1; + } + return 0; + } + + const fileStatesPayload = Array.isArray(fileStates) ? fileStates : [fileStates] + // clear all file states in the previous state of this owner on the files called + fileStatesPayload.forEach((state) => { + state.workspace = workspace + }) + const filteredState = this._fileStates.filter((state) => { + const index = fileStatesPayload.findIndex((payloadFileState: fileDecoration) => { + return payloadFileState.owner == state.owner && payloadFileState.path == state.path + }) + return index == -1 + }) + const newState = [...filteredState, ...fileStatesPayload].sort(sortByPath) + + if (!deepequal(newState, this._fileStates)) { + this._fileStates = newState + this.emit('fileDecoratorsChanged', this._fileStates) + } + } + + async clearFileDecorators() { + this._fileStates = [] + this.emit('fileDecoratorsChanged', []) + } +} \ No newline at end of file diff --git a/libs/remix-ui/file-decorators/.babelrc b/libs/remix-ui/file-decorators/.babelrc new file mode 100644 index 0000000000..09d67939cc --- /dev/null +++ b/libs/remix-ui/file-decorators/.babelrc @@ -0,0 +1,4 @@ +{ + "presets": ["@nrwl/react/babel"], + "plugins": [] +} diff --git a/libs/remix-ui/file-decorators/.eslintrc b/libs/remix-ui/file-decorators/.eslintrc new file mode 100644 index 0000000000..0d43d424e3 --- /dev/null +++ b/libs/remix-ui/file-decorators/.eslintrc @@ -0,0 +1,19 @@ +{ + "env": { + "browser": true, + "es6": true + }, + "extends": "../../../.eslintrc.json", + "globals": { + "Atomics": "readonly", + "SharedArrayBuffer": "readonly" + }, + "parserOptions": { + "ecmaVersion": 11, + "sourceType": "module" + }, + "rules": { + "no-unused-vars": "off", + "@typescript-eslint/no-unused-vars": "error" + } +} diff --git a/libs/remix-ui/file-decorators/src/index.ts b/libs/remix-ui/file-decorators/src/index.ts new file mode 100644 index 0000000000..a116c66cd3 --- /dev/null +++ b/libs/remix-ui/file-decorators/src/index.ts @@ -0,0 +1,3 @@ +export { fileDecoration, fileDecorationType, FileType } from './lib/types/index' +export { FileDecorationIcons } from './lib/components/file-decoration-icon' + diff --git a/libs/remix-ui/file-decorators/src/lib/components/file-decoration-icon.tsx b/libs/remix-ui/file-decorators/src/lib/components/file-decoration-icon.tsx new file mode 100644 index 0000000000..f05cea9f65 --- /dev/null +++ b/libs/remix-ui/file-decorators/src/lib/components/file-decoration-icon.tsx @@ -0,0 +1,51 @@ +// eslint-disable-next-line no-use-before-define +import React, { useEffect, useState } from 'react' + +import { fileDecoration, fileDecorationType, FileType } from '../types' +import FileDecorationCustomIcon from './filedecorationicons/file-decoration-custom-icon' +import FileDecorationErrorIcon from './filedecorationicons/file-decoration-error-icon' +import FileDecorationTooltip from './filedecorationicons/file-decoration-tooltip' +import FileDecorationWarningIcon from './filedecorationicons/file-decoration-warning-icon' + +export type fileDecorationProps = { + file: FileType, + fileDecorations: fileDecoration[] +} + +export const FileDecorationIcons = (props: fileDecorationProps) => { + const [states, setStates] = useState([]) + useEffect(() => { + //console.log(props.file) + //console.log(props.fileState) + setStates(props.fileDecorations.filter((fileDecoration) => fileDecoration.path === props.file.path || `${fileDecoration.workspace.name}/${fileDecoration.path}` === props.file.path)) + }, [props.fileDecorations]) + + + const getTags = function () { + if (states && states.length) { + const elements: JSX.Element[] = [] + + for (const [index, state] of states.entries()) { + switch (state.fileStateType) { + case fileDecorationType.Error: + elements.push(}/>) + break + case fileDecorationType.Warning: + elements.push(}/>) + break + case fileDecorationType.Custom: + elements.push(}/>) + break + } + } + + return elements + } + } + + return <> + {getTags()} + +} + +export default FileDecorationIcons \ No newline at end of file diff --git a/libs/remix-ui/file-decorators/src/lib/components/filedecorationicons/file-decoration-custom-icon.tsx b/libs/remix-ui/file-decorators/src/lib/components/filedecorationicons/file-decoration-custom-icon.tsx new file mode 100644 index 0000000000..c157bc66c0 --- /dev/null +++ b/libs/remix-ui/file-decorators/src/lib/components/filedecorationicons/file-decoration-custom-icon.tsx @@ -0,0 +1,13 @@ +// eslint-disable-next-line no-use-before-define +import React from 'react' +import { fileDecoration } from '../../types' + +const FileDecorationCustomIcon = (props: { + fileDecoration: fileDecoration +}) => { + return <> + {props.fileDecoration.fileStateIcon} + +} + +export default FileDecorationCustomIcon \ No newline at end of file diff --git a/libs/remix-ui/file-decorators/src/lib/components/filedecorationicons/file-decoration-error-icon.tsx b/libs/remix-ui/file-decorators/src/lib/components/filedecorationicons/file-decoration-error-icon.tsx new file mode 100644 index 0000000000..2bb61fd97f --- /dev/null +++ b/libs/remix-ui/file-decorators/src/lib/components/filedecorationicons/file-decoration-error-icon.tsx @@ -0,0 +1,14 @@ +// eslint-disable-next-line no-use-before-define +import React from 'react' + +import { fileDecoration } from '../../types' + +const FileDecorationErrorIcon = (props: { + fileDecoration: fileDecoration +}) => { + return <> + {props.fileDecoration.text} + +} + +export default FileDecorationErrorIcon \ No newline at end of file diff --git a/libs/remix-ui/file-decorators/src/lib/components/filedecorationicons/file-decoration-tooltip.tsx b/libs/remix-ui/file-decorators/src/lib/components/filedecorationicons/file-decoration-tooltip.tsx new file mode 100644 index 0000000000..7c9149c14d --- /dev/null +++ b/libs/remix-ui/file-decorators/src/lib/components/filedecorationicons/file-decoration-tooltip.tsx @@ -0,0 +1,33 @@ +import React from "react"; +import { OverlayTrigger, Tooltip } from "react-bootstrap"; +import { fileDecoration } from "../../types"; + +const FileDecorationTooltip = (props: { + fileDecoration: fileDecoration, + icon: JSX.Element + index: number +}, +) => { + const getComments = function (fileDecoration: fileDecoration) { + if (fileDecoration.commment) { + const commments = Array.isArray(fileDecoration.commment) ? fileDecoration.commment : [fileDecoration.commment] + return commments.map((comment, index) => { + return
{comment}

+ }) + } + } + + return + <>{getComments(props.fileDecoration)} + + } + >
{props.icon}
+ +} + + +export default FileDecorationTooltip; \ No newline at end of file diff --git a/libs/remix-ui/file-decorators/src/lib/components/filedecorationicons/file-decoration-warning-icon.tsx b/libs/remix-ui/file-decorators/src/lib/components/filedecorationicons/file-decoration-warning-icon.tsx new file mode 100644 index 0000000000..1c027c9854 --- /dev/null +++ b/libs/remix-ui/file-decorators/src/lib/components/filedecorationicons/file-decoration-warning-icon.tsx @@ -0,0 +1,11 @@ +// eslint-disable-next-line no-use-before-define +import React from 'react' +import { fileDecoration } from '../../types' + +const FileDecorationWarningIcon = (props: { + fileDecoration: fileDecoration +}) => { + return <>{props.fileDecoration.text} +} + +export default FileDecorationWarningIcon \ No newline at end of file diff --git a/libs/remix-ui/file-decorators/src/lib/helper/index.tsx b/libs/remix-ui/file-decorators/src/lib/helper/index.tsx new file mode 100644 index 0000000000..1afe91b0cf --- /dev/null +++ b/libs/remix-ui/file-decorators/src/lib/helper/index.tsx @@ -0,0 +1,11 @@ +import React from "react" +import { fileDecoration } from "../types" + +export const getComments = function (fileDecoration: fileDecoration) { + if(fileDecoration.commment){ + const commments = Array.isArray(fileDecoration.commment) ? fileDecoration.commment : [fileDecoration.commment] + return commments.map((comment, index) => { + return
{comment}

+ }) + } +} \ No newline at end of file diff --git a/libs/remix-ui/file-decorators/src/lib/types/index.ts b/libs/remix-ui/file-decorators/src/lib/types/index.ts new file mode 100644 index 0000000000..35178721e0 --- /dev/null +++ b/libs/remix-ui/file-decorators/src/lib/types/index.ts @@ -0,0 +1,29 @@ +export enum fileDecorationType { + Error = 'ERROR', + Warning = 'WARNING', + Custom = 'CUSTOM', + None = 'NONE' + } + + export type fileDecoration = { + path: string, + isDirectory: boolean, + fileStateType: fileDecorationType, + fileStateLabelClass: string, + fileStateIconClass: string, + fileStateIcon: string | HTMLDivElement | JSX.Element, + bubble: boolean, + text?: string, + owner: string, + workspace?: any + tooltip?: string + commment?: string[] | string + } + + export interface FileType { + path: string, + name?: string, + isDirectory?: boolean, + type?: 'folder' | 'file' | 'gist', + child?: File[] + } \ No newline at end of file diff --git a/libs/remix-ui/file-decorators/tsconfig.json b/libs/remix-ui/file-decorators/tsconfig.json new file mode 100644 index 0000000000..d52e31ad74 --- /dev/null +++ b/libs/remix-ui/file-decorators/tsconfig.json @@ -0,0 +1,16 @@ +{ + "extends": "../../../tsconfig.base.json", + "compilerOptions": { + "jsx": "react", + "allowJs": true, + "esModuleInterop": true, + "allowSyntheticDefaultImports": true + }, + "files": [], + "include": [], + "references": [ + { + "path": "./tsconfig.lib.json" + } + ] +} diff --git a/libs/remix-ui/file-decorators/tsconfig.lib.json b/libs/remix-ui/file-decorators/tsconfig.lib.json new file mode 100644 index 0000000000..b560bc4dec --- /dev/null +++ b/libs/remix-ui/file-decorators/tsconfig.lib.json @@ -0,0 +1,13 @@ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "outDir": "../../../dist/out-tsc", + "types": ["node"] + }, + "files": [ + "../../../node_modules/@nrwl/react/typings/cssmodule.d.ts", + "../../../node_modules/@nrwl/react/typings/image.d.ts" + ], + "exclude": ["**/*.spec.ts", "**/*.spec.tsx"], + "include": ["**/*.js", "**/*.jsx", "**/*.ts", "**/*.tsx"] +} diff --git a/libs/remix-ui/tabs/src/lib/remix-ui-tabs.tsx b/libs/remix-ui/tabs/src/lib/remix-ui-tabs.tsx index 499d0d1eb1..67c7224c96 100644 --- a/libs/remix-ui/tabs/src/lib/remix-ui-tabs.tsx +++ b/libs/remix-ui/tabs/src/lib/remix-ui-tabs.tsx @@ -1,25 +1,63 @@ + +import { fileDecoration, FileDecorationIcons } from '@remix-ui/file-decorators' +import { Plugin } from '@remixproject/engine' + import React, { useState, useRef, useEffect, useReducer } from 'react' // eslint-disable-line import { Tab, Tabs, TabList, TabPanel } from 'react-tabs' import './remix-ui-tabs.css' /* eslint-disable-next-line */ export interface TabsUIProps { - tabs: Array - onSelect: (index: number) => void - onClose: (index: number) => void - onZoomOut: () => void - onZoomIn: () => void - onReady: (api: any) => void - themeQuality: string + tabs: Array + plugin: Plugin, + onSelect: (index: number) => void + onClose: (index: number) => void + onZoomOut: () => void + onZoomIn: () => void + onReady: (api: any) => void + themeQuality: string } export interface TabsUIApi { - activateTab: (namee: string) => void - active: () => string + activateTab: (namee: string) => void + active: () => string +} + +interface ITabsState { + selectedIndex: number, + fileDecorations: fileDecoration[], +} + +interface ITabsAction { + type: string, + payload: any, +} + + +const initialTabsState: ITabsState = { + selectedIndex: -1, + fileDecorations: [], +} + +const tabsReducer = (state: ITabsState, action: ITabsAction) => { + switch (action.type) { + case 'SELECT_INDEX': + return { + ...state, + selectedIndex: action.payload, + } + case 'SET_FILE_DECORATIONS': + return { + ...state, + fileDecorations: action.payload as fileDecoration[], + } + default: + return state + } } export const TabsUI = (props: TabsUIProps) => { - const [selectedIndex, setSelectedIndex] = useState(-1) + const [tabsState, dispatch] = useReducer(tabsReducer, initialTabsState); const currentIndexRef = useRef(-1) const tabsRef = useRef({}) const tabsElement = useRef(null) @@ -28,10 +66,24 @@ export const TabsUI = (props: TabsUIProps) => { tabs.current = props.tabs // we do this to pass the tabs list to the onReady callbacks useEffect(() => { - if (props.tabs[selectedIndex]) { - tabsRef.current[selectedIndex].scrollIntoView({ behavior: 'smooth', block: 'center' }) + if (props.tabs[tabsState.selectedIndex]) { + tabsRef.current[tabsState.selectedIndex].scrollIntoView({ behavior: 'smooth', block: 'center' }) } - }, [selectedIndex]) + }, [tabsState.selectedIndex]) + + + + const getFileDecorationClasses = (tab: any) => { + const fileDecoration = tabsState.fileDecorations.find((fileDecoration: fileDecoration) => { + if(`${fileDecoration.workspace.name}/${fileDecoration.path}` === tab.name) return true + }) + return fileDecoration && fileDecoration.fileStateLabelClass + } + + const getFileDecorationIcons = (tab: any) => { + return + } + const renderTab = (tab, index) => { const classNameImg = 'my-1 mr-1 text-dark ' + tab.iconClass @@ -40,8 +92,9 @@ export const TabsUI = (props: TabsUIProps) => { return (
{ tabsRef.current[index] = el }} className={classNameTab} data-id={index === currentIndexRef.current ? 'tab-active' : ''} title={tab.tooltip}> - {tab.icon ? () : ()} - {tab.title} + {tab.icon ? () : ()} + {tab.title} + {getFileDecorationIcons(tab)} { props.onClose(index); event.stopPropagation() }}> @@ -56,7 +109,11 @@ export const TabsUI = (props: TabsUIProps) => { const activateTab = (name: string) => { const index = tabs.current.findIndex((tab) => tab.name === name) currentIndexRef.current = index - setSelectedIndex(index) + dispatch({ type: 'SELECT_INDEX', payload: index }) + } + + const setFileDecorations = (fileStates: fileDecoration[]) => { + dispatch({ type: 'SET_FILE_DECORATIONS', payload: fileStates }) } const transformScroll = (event) => { @@ -71,21 +128,23 @@ export const TabsUI = (props: TabsUIProps) => { useEffect(() => { props.onReady({ activateTab, - active + active, + setFileDecorations }) + return () => { tabsElement.current.removeEventListener('wheel', transformScroll) } }, []) return (
-
+
props.onZoomOut()}> props.onZoomIn()}>
{ if (tabsElement.current) return tabsElement.current = domEl @@ -94,7 +153,7 @@ export const TabsUI = (props: TabsUIProps) => { onSelect={(index) => { props.onSelect(index) currentIndexRef.current = index - setSelectedIndex(index) + dispatch({ type: 'SELECT_INDEX', payload: index }) }} > diff --git a/libs/remix-ui/workspace/src/index.ts b/libs/remix-ui/workspace/src/index.ts index 166467d115..c9e5b5355b 100644 --- a/libs/remix-ui/workspace/src/index.ts +++ b/libs/remix-ui/workspace/src/index.ts @@ -1,2 +1,3 @@ export * from './lib/providers/FileSystemProvider' export * from './lib/contexts' +export { FileType } from './lib/types/index' \ No newline at end of file diff --git a/libs/remix-ui/workspace/src/lib/actions/events.ts b/libs/remix-ui/workspace/src/lib/actions/events.ts index a8bde65fd5..62092df30f 100644 --- a/libs/remix-ui/workspace/src/lib/actions/events.ts +++ b/libs/remix-ui/workspace/src/lib/actions/events.ts @@ -1,7 +1,8 @@ +import { fileDecoration } from '@remix-ui/file-decorators' import { extractParentFromKey } from '@remix-ui/helper' import React from 'react' import { action, WorkspaceTemplate } from '../types' -import { displayNotification, displayPopUp, fileAddedSuccess, fileRemovedSuccess, fileRenamedSuccess, folderAddedSuccess, loadLocalhostError, loadLocalhostRequest, loadLocalhostSuccess, removeContextMenuItem, removeFocus, rootFolderChangedSuccess, setContextMenuItem, setMode, setReadOnlyMode } from './payload' +import { displayNotification, displayPopUp, fileAddedSuccess, fileRemovedSuccess, fileRenamedSuccess, folderAddedSuccess, loadLocalhostError, loadLocalhostRequest, loadLocalhostSuccess, removeContextMenuItem, removeFocus, rootFolderChangedSuccess, setContextMenuItem, setMode, setReadOnlyMode, setFileDecorationSuccess } from './payload' import { addInputField, createWorkspace, deleteWorkspace, fetchWorkspaceDirectory, renameWorkspace, switchToWorkspace, uploadFile } from './workspace' const LOCALHOST = ' - connect to localhost - ' @@ -38,6 +39,10 @@ export const listenOnPluginEvents = (filePanelPlugin) => { uploadFile(target, dir, cb) }) + plugin.on('fileDecorator', 'fileDecoratorsChanged', async (items: fileDecoration[]) => { + setFileDecorators(items) + }) + plugin.on('remixd', 'rootFolderChanged', async (path: string) => { rootFolderChanged(path) }) @@ -202,3 +207,8 @@ const fileRenamed = async (oldPath: string) => { const rootFolderChanged = async (path) => { await dispatch(rootFolderChangedSuccess(path)) } + +const setFileDecorators = async (items: fileDecoration[], cb?: (err: Error, result?: string | number | boolean | Record) => void) => { + await dispatch(setFileDecorationSuccess(items)) + cb && cb(null, true) +} diff --git a/libs/remix-ui/workspace/src/lib/actions/payload.ts b/libs/remix-ui/workspace/src/lib/actions/payload.ts index b663f60cfe..8c3fb8fc18 100644 --- a/libs/remix-ui/workspace/src/lib/actions/payload.ts +++ b/libs/remix-ui/workspace/src/lib/actions/payload.ts @@ -1,3 +1,4 @@ +import { fileDecoration } from '@remix-ui/file-decorators' import { action } from '../types' export const setCurrentWorkspace = (workspace: { name: string; isGitRepo: boolean; }) => { @@ -240,6 +241,12 @@ export const fsInitializationCompleted = () => { } } +export const setFileDecorationSuccess = (items: fileDecoration[]) => { + return { + type: 'SET_FILE_DECORATION_SUCCESS', + payload: items + } +} export const cloneRepositoryRequest = () => { return { type: 'CLONE_REPOSITORY_REQUEST' diff --git a/libs/remix-ui/workspace/src/lib/components/file-explorer.tsx b/libs/remix-ui/workspace/src/lib/components/file-explorer.tsx index a0351ea3a1..3ccefdb2db 100644 --- a/libs/remix-ui/workspace/src/lib/components/file-explorer.tsx +++ b/libs/remix-ui/workspace/src/lib/components/file-explorer.tsx @@ -12,7 +12,7 @@ import { checkSpecialChars, extractNameFromKey, extractParentFromKey, joinPath } import { FileRender } from './file-render' export const FileExplorer = (props: FileExplorerProps) => { - const { name, contextMenuItems, removedContextMenuItems, files } = props + const { name, contextMenuItems, removedContextMenuItems, files, fileState } = props const [state, setState] = useState({ ctrlKey: false, newFileName: '', @@ -432,6 +432,7 @@ export const FileExplorer = (props: FileExplorerProps) => { { files[props.name] && Object.keys(files[props.name]).map((key, index) => void } export const FileLabel = (props: FileLabelProps) => { - const { file, focusEdit, editModeOff } = props + const { file, focusEdit, editModeOff, fileDecorations } = props const [isEditable, setIsEditable] = useState(false) + const [fileStateClasses, setFileStateClasses] = useState('') const labelRef = useRef(null) useEffect(() => { @@ -24,6 +27,18 @@ export const FileLabel = (props: FileLabelProps) => { } }, [file.path, focusEdit]) + useEffect(() => { + const state = props.fileDecorations.find((state: fileDecoration) => { + if(state.path === props.file.path) return true + if(state.bubble && props.file.isDirectory && state.path.startsWith(props.file.path)) return true + }) + if (state && state.fileStateLabelClass) { + setFileStateClasses(state.fileStateLabelClass) + } else{ + setFileStateClasses('') + } + }, [fileDecorations]) + useEffect(() => { if (labelRef.current) { setTimeout(() => { @@ -57,10 +72,10 @@ export const FileLabel = (props: FileLabelProps) => { > - { file.name } + {file.name}
) diff --git a/libs/remix-ui/workspace/src/lib/components/file-render.tsx b/libs/remix-ui/workspace/src/lib/components/file-render.tsx index ac0054e96b..164689899b 100644 --- a/libs/remix-ui/workspace/src/lib/components/file-render.tsx +++ b/libs/remix-ui/workspace/src/lib/components/file-render.tsx @@ -6,6 +6,11 @@ import { TreeView, TreeViewItem } from '@remix-ui/tree-view' import { getPathIcon } from '@remix-ui/helper' // eslint-disable-next-line @typescript-eslint/no-unused-vars import { FileLabel } from './file-label' +import { fileDecoration, FileDecorationIcons } from '@remix-ui/file-decorators' + + + + export interface RenderFileProps { file: FileType, @@ -19,6 +24,7 @@ export interface RenderFileProps { handleClickFolder: (path: string, type: string) => void, handleClickFile: (path: string, type: string) => void, handleContextMenu: (pageX: number, pageY: number, path: string, content: string, type: string) => void + fileDecorations: fileDecoration[] } export const FileRender = (props: RenderFileProps) => { @@ -76,7 +82,7 @@ export const FileRender = (props: RenderFileProps) => { iconX='pr-3 fa fa-folder' iconY='pr-3 fa fa-folder-open' key={`${file.path + props.index}`} - label={} + label={} onClick={handleFolderClick} onContextMenu={handleContextMenu} labelClass={labelClass} @@ -89,6 +95,7 @@ export const FileRender = (props: RenderFileProps) => { file.child ? { Object.keys(file.child).map((key, index) => { } + label={ + <> +
+ + +
+ + } onClick={handleFileClick} onContextMenu={handleContextMenu} icon={icon} diff --git a/libs/remix-ui/workspace/src/lib/reducers/workspace.ts b/libs/remix-ui/workspace/src/lib/reducers/workspace.ts index 39f56a202f..0a774e6b03 100644 --- a/libs/remix-ui/workspace/src/lib/reducers/workspace.ts +++ b/libs/remix-ui/workspace/src/lib/reducers/workspace.ts @@ -1,9 +1,10 @@ import { extractNameFromKey } from '@remix-ui/helper' import { action, FileType } from '../types' import * as _ from 'lodash' +import { fileDecoration } from '@remix-ui/file-decorators' interface Action { - type: string - payload: any + type: string + payload: any } export interface BrowserState { browser: { @@ -25,7 +26,8 @@ export interface BrowserState { registeredMenuItems: action[], removedMenuItems: action[], error: string - } + }, + fileState: fileDecoration[] }, localhost: { sharedFolder: string, @@ -40,7 +42,8 @@ export interface BrowserState { registeredMenuItems: action[], removedMenuItems: action[], error: string - } + }, + fileState: [] }, mode: 'browser' | 'localhost', notification: { @@ -75,7 +78,8 @@ export const browserInitialState: BrowserState = { registeredMenuItems: [], removedMenuItems: [], error: null - } + }, + fileState: [] }, localhost: { sharedFolder: '', @@ -90,14 +94,15 @@ export const browserInitialState: BrowserState = { registeredMenuItems: [], removedMenuItems: [], error: null - } + }, + fileState: [] }, mode: 'browser', notification: { title: '', message: '', - actionOk: () => {}, - actionCancel: () => {}, + actionOk: () => { }, + actionCancel: () => { }, labelOk: '', labelCancel: '' }, @@ -650,6 +655,16 @@ export const browserReducer = (state = browserInitialState, action: Action) => { } } + case 'SET_FILE_DECORATION_SUCCESS': { + return { + ...state, + browser: { + ...state.browser, + fileState: action.payload + } + } + } + default: throw new Error() } diff --git a/libs/remix-ui/workspace/src/lib/remix-ui-workspace.tsx b/libs/remix-ui/workspace/src/lib/remix-ui-workspace.tsx index e25811df68..353e4ccf91 100644 --- a/libs/remix-ui/workspace/src/lib/remix-ui-workspace.tsx +++ b/libs/remix-ui/workspace/src/lib/remix-ui-workspace.tsx @@ -307,6 +307,7 @@ export function Workspace () { contextMenuItems={global.fs.browser.contextMenu.registeredMenuItems} removedContextMenuItems={global.fs.browser.contextMenu.removedMenuItems} files={global.fs.browser.files} + fileState={global.fs.browser.fileState} expandPath={global.fs.browser.expandPath} focusEdit={global.fs.focusEdit} focusElement={global.fs.focusElement} @@ -343,6 +344,7 @@ export function Workspace () { contextMenuItems={global.fs.localhost.contextMenu.registeredMenuItems} removedContextMenuItems={global.fs.localhost.contextMenu.removedMenuItems} files={global.fs.localhost.files} + fileState={[]} expandPath={global.fs.localhost.expandPath} focusEdit={global.fs.focusEdit} focusElement={global.fs.focusElement} diff --git a/libs/remix-ui/workspace/src/lib/types/index.ts b/libs/remix-ui/workspace/src/lib/types/index.ts index fb169db5e8..93b6d9f8c9 100644 --- a/libs/remix-ui/workspace/src/lib/types/index.ts +++ b/libs/remix-ui/workspace/src/lib/types/index.ts @@ -1,5 +1,6 @@ import React from 'react' import { customAction } from '@remixproject/plugin-api/lib/file-system/file-panel' +import { fileDecoration } from '@remix-ui/file-decorators'; export type action = { name: string, type?: Array<'folder' | 'gist' | 'file'>, path?: string[], extension?: string[], pattern?: string[], id: string, multiselect: boolean, label: string, sticky?: boolean } export interface JSONStandardInput { @@ -73,6 +74,7 @@ export interface FileExplorerProps { contextMenuItems: MenuItems, removedContextMenuItems: MenuItems, files: { [x: string]: Record }, + fileState: fileDecoration[], expandPath: string[], focusEdit: string, focusElement: { key: string, type: 'file' | 'folder' | 'gist' }[], From 53e9650cca64e43481a397f4d01b92d11a07c6e6 Mon Sep 17 00:00:00 2001 From: filip mertens Date: Thu, 21 Jul 2022 15:39:15 +0200 Subject: [PATCH 02/10] add test --- .../src/tests/editorAutoComplete.test.ts | 518 ++++++++++++++++++ 1 file changed, 518 insertions(+) create mode 100644 apps/remix-ide-e2e/src/tests/editorAutoComplete.test.ts diff --git a/apps/remix-ide-e2e/src/tests/editorAutoComplete.test.ts b/apps/remix-ide-e2e/src/tests/editorAutoComplete.test.ts new file mode 100644 index 0000000000..8dc7bd4440 --- /dev/null +++ b/apps/remix-ide-e2e/src/tests/editorAutoComplete.test.ts @@ -0,0 +1,518 @@ +'use strict' +import { NightwatchBrowser } from 'nightwatch' +import init from '../helpers/init' +import examples from '../examples/editor-test-contracts' + +const autoCompleteLineElement = (name: string) => { + return `//*[@class='editor-widget suggest-widget visible']//*[@class='contents' and contains(.,'${name}')]` +} + +module.exports = { + '@disabled': true, + before: function (browser: NightwatchBrowser, done: VoidFunction) { + init(browser, done, 'http://127.0.0.1:8080', false) + }, + 'Should add test and base files #group2': function (browser: NightwatchBrowser) { + browser.addFile(examples.testContract.name, examples.testContract) + .addFile(examples.baseContract.name, examples.baseContract) + .addFile(examples.import1Contract.name, examples.import1Contract) + .addFile(examples.baseOfBaseContract.name, examples.baseOfBaseContract) + .addFile(examples.secondimport.name, examples.secondimport) + .addFile(examples.importbase.name, examples.importbase) + .openFile(examples.testContract.name) + }, + 'Should put cursor in the () of the function #group2': function (browser: NightwatchBrowser) { + browser.scrollToLine(36) + const path = "//*[@class='view-line' and contains(.,'myprivatefunction') and contains(.,'private')]//span//span[contains(.,'(')]" + browser.waitForElementVisible('#editorView') + .useXpath() + .click(path).pause(1000) + }, + 'Should complete variable declaration types in a function definition #group2': function (browser: NightwatchBrowser) { + browser + .perform(function () { + const actions = this.actions({ async: true }); + return actions. + sendKeys('uint25') + }) + .waitForElementPresent(autoCompleteLineElement('uint256')) + .click(autoCompleteLineElement('uint256')) + .perform(function () { + const actions = this.actions({ async: true }); + return actions. + sendKeys(' abc') + .sendKeys(this.Keys.ENTER) // we need to split lines for FF texts to pass because the screen is too narrow + .sendKeys(', testb') + }) + .waitForElementPresent(autoCompleteLineElement('"TestBookDefinition"')) + .click(autoCompleteLineElement('"TestBookDefinition"')) + .perform(function () { + const actions = this.actions({ async: true }); + return actions. + sendKeys(' memo') + }) + .waitForElementPresent(autoCompleteLineElement('memory')) + .click(autoCompleteLineElement('memory')) + .perform(function () { + const actions = this.actions({ async: true }); + return actions. + sendKeys(' btextbook') + .sendKeys(this.Keys.ENTER) + .sendKeys(', BaseB') + }) + .waitForElementPresent(autoCompleteLineElement('"BaseBook"')) + .click(autoCompleteLineElement('"BaseBook"')) + .perform(function () { + const actions = this.actions({ async: true }); + return actions. + sendKeys(' stor') + }) + .waitForElementPresent(autoCompleteLineElement('storage')) + .click(autoCompleteLineElement('storage')) + .perform(function () { + const actions = this.actions({ async: true }); + return actions. + sendKeys(' localbbook') + }).pause(3000) + }, + 'Should put cursor at the end of function #group2': function (browser: NightwatchBrowser) { + + const path = "//*[@class='view-line' and contains(.,'localbbook') and contains(.,'private')]//span//span[contains(.,'{')]" + browser + .useXpath() + .click(path).pause(1000) + .perform(function () { + const actions = this.actions({ async: true }); + return actions. + // right arrow key + sendKeys(this.Keys.ARROW_RIGHT). + sendKeys(this.Keys.ARROW_RIGHT) + }) + }, + 'Should autcomplete address types': function (browser: NightwatchBrowser) { + browser + .perform(function () { + const actions = this.actions({ async: true }); + return actions. + sendKeys('addre') + }) + .waitForElementPresent(autoCompleteLineElement('address')) + .click(autoCompleteLineElement('address')) + .perform(function () { + const actions = this.actions({ async: true }); + return actions. + sendKeys(' someaddress;') + .sendKeys(this.Keys.ENTER) + }).pause(2000) + .perform(function () { + const actions = this.actions({ async: true }); + return actions. + sendKeys('someaddress.') + }) + .waitForElementVisible(autoCompleteLineElement('balance')) + .waitForElementVisible(autoCompleteLineElement('send')) + .waitForElementVisible(autoCompleteLineElement('transfer')) + .waitForElementVisible(autoCompleteLineElement('code')) + .click(autoCompleteLineElement('balance')) + .perform(function () { + const actions = this.actions({ async: true }); + return actions + .sendKeys(this.Keys.ENTER) + }) + }, + 'Should autcomplete array types': function (browser: NightwatchBrowser) { + browser + .perform(function () { + const actions = this.actions({ async: true }); + return actions. + sendKeys('uin') + }) + .waitForElementPresent(autoCompleteLineElement('uint')) + .click(autoCompleteLineElement('uint')) + .perform(function () { + const actions = this.actions({ async: true }); + return actions. + sendKeys('[] mem') + }) + .waitForElementVisible(autoCompleteLineElement('memory')) + .click(autoCompleteLineElement('memory')) + .perform(function () { + const actions = this.actions({ async: true }); + return actions. + sendKeys(' somearray;') + } + ).pause(2000) + .perform(function () { + const actions = this.actions({ async: true }); + return actions + .sendKeys(this.Keys.ENTER) + .sendKeys('somearray.') + }) + .waitForElementVisible(autoCompleteLineElement('push')) + .waitForElementVisible(autoCompleteLineElement('pop')) + .waitForElementVisible(autoCompleteLineElement('length')) + .click(autoCompleteLineElement('length')) + .perform(function () { + const actions = this.actions({ async: true }); + return actions + .sendKeys(this.Keys.ENTER) + }) + }, + 'Should see and autocomplete second import because it was imported by the first import #group2': function (browser: NightwatchBrowser) { + browser + .perform(function () { + const actions = this.actions({ async: true }); + return actions. + sendKeys('secondi') + }) + .waitForElementPresent(autoCompleteLineElement('secondimport')) + .click(autoCompleteLineElement('secondimport')) + .perform(function () { + const actions = this.actions({ async: true }); + return actions. + sendKeys(' sec;') + .sendKeys(this.Keys.ENTER) + }).pause(3000) + .perform(function () { + const actions = this.actions({ async: true }); + return actions. + sendKeys('sec.') + }) + .waitForElementVisible(autoCompleteLineElement('secondimportstring')) + .click(autoCompleteLineElement('secondimportstring')) + .perform(function () { + const actions = this.actions({ async: true }); + return actions. + sendKeys(';') + .sendKeys(this.Keys.ENTER) + }) + + }, + 'Should see and autocomplete imported local class #group2': function (browser: NightwatchBrowser) { + browser + .perform(function () { + const actions = this.actions({ async: true }); + return actions. + sendKeys('import') + }) + .waitForElementPresent(autoCompleteLineElement('importedcontract')) + .click(autoCompleteLineElement('importedcontract')) + .perform(function () { + const actions = this.actions({ async: true }); + return actions. + sendKeys('.') + }) + .waitForElementVisible(autoCompleteLineElement('externalimport')) + .waitForElementVisible(autoCompleteLineElement('importbasestring')) + .waitForElementVisible(autoCompleteLineElement('importedbook')) + .waitForElementVisible(autoCompleteLineElement('importpublicstring')) + .waitForElementVisible(autoCompleteLineElement('publicimport')) + // no private + .waitForElementNotPresent(autoCompleteLineElement('importprivatestring')) + .waitForElementNotPresent(autoCompleteLineElement('privateimport')) + // no internal + .waitForElementNotPresent(autoCompleteLineElement('importinternalstring')) + .waitForElementNotPresent(autoCompleteLineElement('internalimport')) + .click(autoCompleteLineElement('importbasestring')) + .perform(function () { + const actions = this.actions({ async: true }); + return actions. + sendKeys(';') + .sendKeys(this.Keys.ENTER) + }) + + }, + 'Should autocomplete derived and local event when not using this. #group2': function (browser: NightwatchBrowser) { + browser.perform(function () { + const actions = this.actions({ async: true }); + return actions. + sendKeys('emit base') + }) + .waitForElementVisible(autoCompleteLineElement('BaseEvent')) + .click(autoCompleteLineElement('BaseEvent')) + .perform(function () { + const actions = this.actions({ async: true }); + return actions + .sendKeys('msg.sender') + .sendKeys(this.Keys.TAB) + .sendKeys(this.Keys.TAB) // somehow this is needed to get the cursor to the next parameter, only for selenium + .sendKeys('3232') + .sendKeys(this.Keys.TAB) + .sendKeys(this.Keys.ENTER) + }) + .perform(function () { + const actions = this.actions({ async: true }); + return actions. + sendKeys('emit MyEv') + }) + .waitForElementVisible(autoCompleteLineElement('MyEvent')) + .click(autoCompleteLineElement('MyEvent')) + .perform(function () { + const actions = this.actions({ async: true }); + return actions + .sendKeys('3232') + .sendKeys(this.Keys.TAB) + .sendKeys(this.Keys.ENTER) + }) + }, + + 'Should type and get msg options #group2': function (browser: NightwatchBrowser) { + browser. + perform(function () { + const actions = this.actions({ async: true }); + return actions. + sendKeys(this.Keys.ENTER). + sendKeys('msg.') + }) + .waitForElementVisible(autoCompleteLineElement('sender')) + .waitForElementVisible(autoCompleteLineElement('data')) + .waitForElementVisible(autoCompleteLineElement('value')) + .waitForElementVisible(autoCompleteLineElement('gas')) + .waitForElementVisible(autoCompleteLineElement('sig')) + .click(autoCompleteLineElement('sender')) + .perform(function () { + const actions = this.actions({ async: true }); + return actions. + sendKeys('.') + }) + .waitForElementVisible(autoCompleteLineElement('balance')) + .waitForElementVisible(autoCompleteLineElement('code')) + .waitForElementVisible(autoCompleteLineElement('codehash')) + .waitForElementVisible(autoCompleteLineElement('send')) + .waitForElementVisible(autoCompleteLineElement('transfer')) + .click(autoCompleteLineElement('balance')) + .perform(function () { + const actions = this.actions({ async: true }); + return actions. + sendKeys(this.Keys.ENTER) + }) + }, + 'Should bo and get book #group2': function (browser: NightwatchBrowser) { + browser. + perform(function () { + const actions = this.actions({ async: true }); + return actions. + sendKeys(this.Keys.ENTER). + sendKeys('bo') + }) + .waitForElementVisible(autoCompleteLineElement('book')) + .click(autoCompleteLineElement('book')) + }, + 'Should autcomplete derived struct #group2': function (browser: NightwatchBrowser) { + browser.perform(function () { + const actions = this.actions({ async: true }); + return actions. + sendKeys('.') + }) + .waitForElementVisible(autoCompleteLineElement('author')) + .waitForElementVisible(autoCompleteLineElement('book_id')) + .waitForElementVisible(autoCompleteLineElement('title')) + .click(autoCompleteLineElement('title')) + .perform(function () { + const actions = this.actions({ async: true }); + return actions. + sendKeys(';') + .sendKeys(this.Keys.ENTER) + }) + }, + 'Should bo and get basebook #group2': function (browser: NightwatchBrowser) { + browser. + perform(function () { + const actions = this.actions({ async: true }); + return actions. + sendKeys(this.Keys.ENTER). + sendKeys('base') + }) + .waitForElementVisible(autoCompleteLineElement('basebook')) + .click(autoCompleteLineElement('basebook')) + }, + 'Should autcomplete derived struct from base class #group2': function (browser: NightwatchBrowser) { + browser.perform(function () { + const actions = this.actions({ async: true }); + return actions. + sendKeys('.') + }) + .waitForElementVisible(autoCompleteLineElement('author')) + .waitForElementVisible(autoCompleteLineElement('book_id')) + .waitForElementVisible(autoCompleteLineElement('title')) + .click(autoCompleteLineElement('title')) + .perform(function () { + const actions = this.actions({ async: true }); + return actions. + sendKeys(';') + .sendKeys(this.Keys.ENTER) + }) + }, + 'Should block scoped localbbook #group2': function (browser: NightwatchBrowser) { + browser.pause(4000). + perform(function () { + const actions = this.actions({ async: true }); + return actions. + sendKeys(this.Keys.ENTER). + sendKeys('localb') + }) + .waitForElementVisible(autoCompleteLineElement('localbbook')) + .click(autoCompleteLineElement('localbbook')) + }, + 'Should autcomplete derived struct from block localbbook #group2': function (browser: NightwatchBrowser) { + browser.perform(function () { + const actions = this.actions({ async: true }); + return actions. + sendKeys('.') + }) + .waitForElementVisible(autoCompleteLineElement('author')) + .waitForElementVisible(autoCompleteLineElement('book_id')) + .waitForElementVisible(autoCompleteLineElement('title')) + .click(autoCompleteLineElement('title')) + .perform(function () { + const actions = this.actions({ async: true }); + return actions. + sendKeys(';') + .sendKeys(this.Keys.ENTER) + }) + }, + 'Should block scoped btextbook #group2': function (browser: NightwatchBrowser) { + browser. + perform(function () { + const actions = this.actions({ async: true }); + return actions. + sendKeys(this.Keys.ENTER). + sendKeys('btext') + }) + .waitForElementVisible(autoCompleteLineElement('btextbook')) + .click(autoCompleteLineElement('btextbook')) + }, + 'Should autcomplete derived struct from block btextbook #group2': function (browser: NightwatchBrowser) { + browser.perform(function () { + const actions = this.actions({ async: true }); + return actions. + sendKeys('.') + }) + .waitForElementVisible(autoCompleteLineElement('author')) + .waitForElementVisible(autoCompleteLineElement('book_id')) + .waitForElementVisible(autoCompleteLineElement('title')) + .click(autoCompleteLineElement('title')) + .perform(function () { + const actions = this.actions({ async: true }); + return actions. + sendKeys(';') + .sendKeys(this.Keys.ENTER) + }) + }, + 'Should find private and internal local functions #group2': function (browser: NightwatchBrowser) { + browser.perform(function () { + const actions = this.actions({ async: true }); + return actions. + sendKeys('my') + }) + .waitForElementVisible(autoCompleteLineElement('myprivatefunction')) + .waitForElementVisible(autoCompleteLineElement('myinternalfunction')) + .waitForElementVisible(autoCompleteLineElement('memory')) + .click(autoCompleteLineElement('myinternalfunction')) + .perform(function () { + const actions = this.actions({ async: true }); + return actions. + sendKeys(this.Keys.ENTER) + }) + }, + 'Should find internal functions and var from base and owner #group2': function (browser: NightwatchBrowser) { + browser.perform(function () { + const actions = this.actions({ async: true }); + return actions. + sendKeys('intern') + }) + .waitForElementVisible(autoCompleteLineElement('internalbasefunction')) + .waitForElementVisible(autoCompleteLineElement('internalstring')) + .waitForElementVisible(autoCompleteLineElement('internalbasestring')) + // keyword internal + .waitForElementVisible(autoCompleteLineElement('internal keyword')) + .click(autoCompleteLineElement('internalbasefunction')) + .perform(function () { + const actions = this.actions({ async: true }); + return actions + .sendKeys(this.Keys.ENTER) + }) + }, + + 'Should not find external functions without this. #group2': function (browser: NightwatchBrowser) { + browser.perform(function () { + const actions = this.actions({ async: true }); + return actions. + sendKeys('extern') + }) + .waitForElementNotPresent(autoCompleteLineElement('externalbasefunction')) + .waitForElementNotPresent(autoCompleteLineElement('myexternalfunction')) + // keyword internal + .waitForElementVisible(autoCompleteLineElement('external keyword')) + .perform(function () { + const actions = this.actions({ async: true }); + return actions + .sendKeys(this.Keys.BACK_SPACE) + .sendKeys(this.Keys.BACK_SPACE) + .sendKeys(this.Keys.BACK_SPACE) + .sendKeys(this.Keys.BACK_SPACE) + .sendKeys(this.Keys.BACK_SPACE) + .sendKeys(this.Keys.BACK_SPACE) + .sendKeys(this.Keys.BACK_SPACE) + }) + }, + 'Should find external functions using this. #group2': function (browser: NightwatchBrowser) { + browser. + perform(function () { + const actions = this.actions({ async: true }); + return actions. + sendKeys(this.Keys.ENTER). + sendKeys('this.') + }) + .waitForElementVisible(autoCompleteLineElement('externalbasefunction')) + .waitForElementVisible(autoCompleteLineElement('myexternalfunction')) + }, + 'Should find public functions and vars using this. but not private & other types of nodes #group2': function (browser: NightwatchBrowser) { + browser + .waitForElementVisible(autoCompleteLineElement('"publicbasefunction"')) + .waitForElementVisible(autoCompleteLineElement('"publicstring"')) + .waitForElementVisible(autoCompleteLineElement('"basebook"')) + .waitForElementVisible(autoCompleteLineElement('"mybook"')) + .waitForElementVisible(autoCompleteLineElement('"testing"')) + // but no private functions or vars or other types of nodes + .waitForElementNotPresent(autoCompleteLineElement('"private"')) + .waitForElementNotPresent(autoCompleteLineElement('"BaseEvent"')) + .waitForElementNotPresent(autoCompleteLineElement('"BaseEnum"')) + .waitForElementNotPresent(autoCompleteLineElement('"TestBookDefinition"')) + .click(autoCompleteLineElement('"publicbasefunction"')) + .perform(function () { + const actions = this.actions({ async: true }); + return actions + .sendKeys(this.Keys.ENTER) + }) + }, + 'Should autocomplete local and derived ENUMS #group2': function (browser: NightwatchBrowser) { + browser.perform(function () { + const actions = this.actions({ async: true }); + return actions. + sendKeys('BaseEnum.') + }) + .waitForElementVisible(autoCompleteLineElement('SMALL')) + .waitForElementVisible(autoCompleteLineElement('MEDIUM')) + .waitForElementVisible(autoCompleteLineElement('LARGE')) + .click(autoCompleteLineElement('SMALL')) + .perform(function () { + const actions = this.actions({ async: true }); + return actions. + sendKeys(';') + .sendKeys(this.Keys.ENTER) + .sendKeys('MyEnum.') + }) + .waitForElementVisible(autoCompleteLineElement('SMALL')) + .waitForElementVisible(autoCompleteLineElement('MEDIUM')) + .waitForElementVisible(autoCompleteLineElement('LARGE')) + .click(autoCompleteLineElement('SMALL')) + .perform(function () { + const actions = this.actions({ async: true }); + return actions. + sendKeys(';') + .sendKeys(this.Keys.ENTER) + }) + } +} + From 53cedb0dd89f7712ae26515dd08587d8f4d24022 Mon Sep 17 00:00:00 2001 From: filip mertens Date: Thu, 21 Jul 2022 15:39:23 +0200 Subject: [PATCH 03/10] add tests --- .../src/tests/file_decorator.test.ts | 97 +++++++++++++++++++ apps/remix-ide/src/app.js | 7 +- .../src/app/plugins/file-decorator.ts | 42 +++++--- .../file-decoration-custom-icon.tsx | 2 +- .../file-decoration-error-icon.tsx | 2 +- .../file-decoration-warning-icon.tsx | 2 +- nx.json | 3 + tsconfig.base.json | 1 + workspace.json | 15 +++ 9 files changed, 153 insertions(+), 18 deletions(-) create mode 100644 apps/remix-ide-e2e/src/tests/file_decorator.test.ts diff --git a/apps/remix-ide-e2e/src/tests/file_decorator.test.ts b/apps/remix-ide-e2e/src/tests/file_decorator.test.ts new file mode 100644 index 0000000000..40e78991b3 --- /dev/null +++ b/apps/remix-ide-e2e/src/tests/file_decorator.test.ts @@ -0,0 +1,97 @@ + +'use strict' +import { NightwatchBrowser } from 'nightwatch' +import init from '../helpers/init' + +module.exports = { + + before: function (browser: NightwatchBrowser, done: VoidFunction) { + init(browser, done) + }, + + 'Test decorators with script': function (browser: NightwatchBrowser) { + browser + .openFile('contracts') + .openFile('contracts/2_Owner.sol') + .openFile('contracts/1_Storage.sol') + .openFile('contracts/3_Ballot.sol') + .addFile('scripts/decorators.ts', { content: testScript }) + .pause(2000) + .executeScript('remix.exeCurrent()') + .pause(4000) + .useXpath() + .waitForElementContainsText('//*[@id="fileExplorerView"]//*[@data-id="file-decoration-error-contracts/2_Owner.sol"]', '2') + .waitForElementContainsText('//*[@class="mainview"]//*[@data-id="file-decoration-error-contracts/2_Owner.sol"]', '2') + .waitForElementContainsText('//*[@id="fileExplorerView"]//*[@data-id="file-decoration-custom-contracts/2_Owner.sol"]', 'U') + .waitForElementContainsText('//*[@class="mainview"]//*[@data-id="file-decoration-custom-contracts/2_Owner.sol"]', 'U') + .waitForElementContainsText('//*[@id="fileExplorerView"]//*[@data-id="file-decoration-warning-contracts/1_Storage.sol"]', '2') + .waitForElementContainsText('//*[@class="mainview"]//*[@data-id="file-decoration-warning-contracts/1_Storage.sol"]', '2') + .useCss() + .waitForElementNotPresent('[data-id="file-decoration-custom-contracts/3_Ballot.sol"]', 10000) + .useXpath() + .moveToElement('//*[@id="fileExplorerView"]//*[@data-id="file-decoration-error-contracts/2_Owner.sol"]', 0,0) + .waitForElementVisible('//*[@id="error-tooltip-contracts/2_Owner.sol"]') + .waitForElementContainsText('//*[@id="error-tooltip-contracts/2_Owner.sol"]', 'error on owner') + } + +} +const testScript = ` +(async () => { + remix.call('fileDecorator' as any, 'clearFileDecorators') + let decorator: any = { + path: 'contracts/2_Owner.sol', + isDirectory: false, + fileStateType: 'ERROR', + fileStateLabelClass: 'text-danger', + fileStateIconClass: '', + fileStateIcon: '', + text: '2', + owner: 'code-parser', + bubble: true, + commment: 'error on owner', + } + let decorator2: any = { + path: 'contracts/2_Owner.sol', + isDirectory: false, + fileStateType: 'CUSTOM', + fileStateLabelClass: 'text-success', + fileStateIconClass: 'text-success', + fileStateIcon: 'U', + text: '', + owner: 'code-parser', + bubble: true, + commment: 'modified', + } + remix.call('fileDecorator' as any, 'setFileDecorators', [decorator, decorator2]) + + decorator = { + path: 'contracts/1_Storage.sol', + isDirectory: false, + fileStateType: 'WARNING', + fileStateLabelClass: 'text-warning', + fileStateIconClass: '', + fileStateIcon: '', + text: '2', + owner: 'code-parser', + bubble: true, + commment: 'warning on storage', + } + remix.call('fileDecorator' as any, 'setFileDecorators', decorator) + + decorator = { + path: 'contracts/3_Ballot.sol', + isDirectory: false, + fileStateType: 'CUSTOM', + fileStateLabelClass: '', + fileStateIconClass: '', + fileStateIcon: 'customtext', + text: 'w', + owner: 'dgit', + bubble: true, + commment: '', + } + remix.call('fileDecorator' as any, 'setFileDecorators', decorator) + + remix.call('fileDecorator' as any, 'clearFileDecorators', 'dgit') + + })()` \ No newline at end of file diff --git a/apps/remix-ide/src/app.js b/apps/remix-ide/src/app.js index d9c93fd4f6..4270ecce92 100644 --- a/apps/remix-ide/src/app.js +++ b/apps/remix-ide/src/app.js @@ -31,6 +31,7 @@ import { FoundryProvider } from './app/tabs/foundry-provider' import { ExternalHttpProvider } from './app/tabs/external-http-provider' import { Injected0ptimismProvider } from './app/tabs/injected-optimism-provider' import { InjectedArbitrumOneProvider } from './app/tabs/injected-arbitrum-one-provider' +import { FileDecorator } from './app/plugins/file-decorator' const isElectron = require('is-electron') @@ -156,6 +157,9 @@ class AppComponent { // ----------------- Storage plugin --------------------------------- const storagePlugin = new StoragePlugin() + // ------- FILE DECORATOR PLUGIN ------------------ + const fileDecorator = new FileDecorator() + //----- search const search = new SearchPlugin() @@ -239,6 +243,7 @@ class AppComponent { fetchAndCompile, dGitProvider, storagePlugin, + fileDecorator, hardhatProvider, ganacheProvider, foundryProvider, @@ -363,7 +368,7 @@ class AppComponent { await this.appManager.activatePlugin(['sidePanel']) // activating host plugin separately await this.appManager.activatePlugin(['home']) await this.appManager.activatePlugin(['settings', 'config']) - await this.appManager.activatePlugin(['hiddenPanel', 'pluginManager', 'contextualListener', 'terminal', 'blockchain', 'fetchAndCompile', 'contentImport', 'gistHandler']) + await this.appManager.activatePlugin(['hiddenPanel', 'pluginManager', 'fileDecorator', 'contextualListener', 'terminal', 'blockchain', 'fetchAndCompile', 'contentImport', 'gistHandler']) await this.appManager.activatePlugin(['settings']) await this.appManager.activatePlugin(['walkthrough','storage', 'search','compileAndRun', 'recorder']) diff --git a/apps/remix-ide/src/app/plugins/file-decorator.ts b/apps/remix-ide/src/app/plugins/file-decorator.ts index 60a14ef47f..5332db1508 100644 --- a/apps/remix-ide/src/app/plugins/file-decorator.ts +++ b/apps/remix-ide/src/app/plugins/file-decorator.ts @@ -18,22 +18,14 @@ export class FileDecorator extends Plugin { constructor() { super(profile) } + + /** * * @param fileStates Array of file states */ async setFileDecorators(fileStates: fileDecoration[] | fileDecoration) { const workspace = await this.call('filePanel', 'getCurrentWorkspace') - function sortByPath( a: fileDecoration, b: fileDecoration ) { - if ( a.path < b.path ){ - return -1; - } - if ( a.path > b.path ){ - return 1; - } - return 0; - } - const fileStatesPayload = Array.isArray(fileStates) ? fileStates : [fileStates] // clear all file states in the previous state of this owner on the files called fileStatesPayload.forEach((state) => { @@ -46,15 +38,37 @@ export class FileDecorator extends Plugin { return index == -1 }) const newState = [...filteredState, ...fileStatesPayload].sort(sortByPath) - + if (!deepequal(newState, this._fileStates)) { this._fileStates = newState this.emit('fileDecoratorsChanged', this._fileStates) } } - async clearFileDecorators() { - this._fileStates = [] - this.emit('fileDecoratorsChanged', []) + async clearFileDecorators(owner? : string) { + if(!owner) { + this._fileStates = [] + this.emit('fileDecoratorsChanged', []) + } else { + const filteredState = this._fileStates.filter((state) => { + return state.owner != owner + }) + const newState = [...filteredState].sort(sortByPath) + + if (!deepequal(newState, this._fileStates)) { + this._fileStates = newState + this.emit('fileDecoratorsChanged', this._fileStates) + } + } + } +} + +const sortByPath = (a: fileDecoration, b: fileDecoration) => { + if (a.path < b.path) { + return -1; + } + if (a.path > b.path) { + return 1; } + return 0; } \ No newline at end of file diff --git a/libs/remix-ui/file-decorators/src/lib/components/filedecorationicons/file-decoration-custom-icon.tsx b/libs/remix-ui/file-decorators/src/lib/components/filedecorationicons/file-decoration-custom-icon.tsx index c157bc66c0..2cfec2ac97 100644 --- a/libs/remix-ui/file-decorators/src/lib/components/filedecorationicons/file-decoration-custom-icon.tsx +++ b/libs/remix-ui/file-decorators/src/lib/components/filedecorationicons/file-decoration-custom-icon.tsx @@ -5,7 +5,7 @@ import { fileDecoration } from '../../types' const FileDecorationCustomIcon = (props: { fileDecoration: fileDecoration }) => { - return <> + return <> {props.fileDecoration.fileStateIcon} } diff --git a/libs/remix-ui/file-decorators/src/lib/components/filedecorationicons/file-decoration-error-icon.tsx b/libs/remix-ui/file-decorators/src/lib/components/filedecorationicons/file-decoration-error-icon.tsx index 2bb61fd97f..5a9c48b555 100644 --- a/libs/remix-ui/file-decorators/src/lib/components/filedecorationicons/file-decoration-error-icon.tsx +++ b/libs/remix-ui/file-decorators/src/lib/components/filedecorationicons/file-decoration-error-icon.tsx @@ -7,7 +7,7 @@ const FileDecorationErrorIcon = (props: { fileDecoration: fileDecoration }) => { return <> - {props.fileDecoration.text} + {props.fileDecoration.text} } diff --git a/libs/remix-ui/file-decorators/src/lib/components/filedecorationicons/file-decoration-warning-icon.tsx b/libs/remix-ui/file-decorators/src/lib/components/filedecorationicons/file-decoration-warning-icon.tsx index 1c027c9854..9bfd368506 100644 --- a/libs/remix-ui/file-decorators/src/lib/components/filedecorationicons/file-decoration-warning-icon.tsx +++ b/libs/remix-ui/file-decorators/src/lib/components/filedecorationicons/file-decoration-warning-icon.tsx @@ -5,7 +5,7 @@ import { fileDecoration } from '../../types' const FileDecorationWarningIcon = (props: { fileDecoration: fileDecoration }) => { - return <>{props.fileDecoration.text} + return <>{props.fileDecoration.text} } export default FileDecorationWarningIcon \ No newline at end of file diff --git a/nx.json b/nx.json index 13cd26c2b2..734bd55a4c 100644 --- a/nx.json +++ b/nx.json @@ -170,6 +170,9 @@ "remix-ui-permission-handler": { "tags": [] }, + "remix-ui-file-decorators": { + "tags": [] + }, "remix-ui-tooltip-popup": { "tags": [] } diff --git a/tsconfig.base.json b/tsconfig.base.json index e97aaf9aff..9174b23427 100644 --- a/tsconfig.base.json +++ b/tsconfig.base.json @@ -88,6 +88,7 @@ "@remix-ui/permission-handler": [ "libs/remix-ui/permission-handler/src/index.ts" ], + "@remix-ui/file-decorators": ["libs/remix-ui/file-decorators/src/index.ts"], "@remix-ui/tooltip-popup": ["libs/remix-ui/tooltip-popup/src/index.ts"] } }, diff --git a/workspace.json b/workspace.json index b1ec5f63d5..fd0935ee57 100644 --- a/workspace.json +++ b/workspace.json @@ -1270,6 +1270,21 @@ } } } + }, + "remix-ui-file-decorators": { + "root": "libs/remix-ui/file-decorators", + "sourceRoot": "libs/remix-ui/file-decorators/src", + "projectType": "library", + "architect": { + "lint": { + "builder": "@nrwl/linter:lint", + "options": { + "linter": "eslint", + "tsConfig": ["libs/remix-ui/file-decorators/tsconfig.lib.json"], + "exclude": ["**/node_modules/**", "!libs/remix-ui/file-decorators/**/*"] + } + } + } } }, "cli": { From d9a09cdc2999dd50f222c9e2432821b5aed9bac6 Mon Sep 17 00:00:00 2001 From: filip mertens Date: Thu, 21 Jul 2022 15:40:27 +0200 Subject: [PATCH 04/10] rm test --- .../src/tests/editorAutoComplete.test.ts | 518 ------------------ 1 file changed, 518 deletions(-) delete mode 100644 apps/remix-ide-e2e/src/tests/editorAutoComplete.test.ts diff --git a/apps/remix-ide-e2e/src/tests/editorAutoComplete.test.ts b/apps/remix-ide-e2e/src/tests/editorAutoComplete.test.ts deleted file mode 100644 index 8dc7bd4440..0000000000 --- a/apps/remix-ide-e2e/src/tests/editorAutoComplete.test.ts +++ /dev/null @@ -1,518 +0,0 @@ -'use strict' -import { NightwatchBrowser } from 'nightwatch' -import init from '../helpers/init' -import examples from '../examples/editor-test-contracts' - -const autoCompleteLineElement = (name: string) => { - return `//*[@class='editor-widget suggest-widget visible']//*[@class='contents' and contains(.,'${name}')]` -} - -module.exports = { - '@disabled': true, - before: function (browser: NightwatchBrowser, done: VoidFunction) { - init(browser, done, 'http://127.0.0.1:8080', false) - }, - 'Should add test and base files #group2': function (browser: NightwatchBrowser) { - browser.addFile(examples.testContract.name, examples.testContract) - .addFile(examples.baseContract.name, examples.baseContract) - .addFile(examples.import1Contract.name, examples.import1Contract) - .addFile(examples.baseOfBaseContract.name, examples.baseOfBaseContract) - .addFile(examples.secondimport.name, examples.secondimport) - .addFile(examples.importbase.name, examples.importbase) - .openFile(examples.testContract.name) - }, - 'Should put cursor in the () of the function #group2': function (browser: NightwatchBrowser) { - browser.scrollToLine(36) - const path = "//*[@class='view-line' and contains(.,'myprivatefunction') and contains(.,'private')]//span//span[contains(.,'(')]" - browser.waitForElementVisible('#editorView') - .useXpath() - .click(path).pause(1000) - }, - 'Should complete variable declaration types in a function definition #group2': function (browser: NightwatchBrowser) { - browser - .perform(function () { - const actions = this.actions({ async: true }); - return actions. - sendKeys('uint25') - }) - .waitForElementPresent(autoCompleteLineElement('uint256')) - .click(autoCompleteLineElement('uint256')) - .perform(function () { - const actions = this.actions({ async: true }); - return actions. - sendKeys(' abc') - .sendKeys(this.Keys.ENTER) // we need to split lines for FF texts to pass because the screen is too narrow - .sendKeys(', testb') - }) - .waitForElementPresent(autoCompleteLineElement('"TestBookDefinition"')) - .click(autoCompleteLineElement('"TestBookDefinition"')) - .perform(function () { - const actions = this.actions({ async: true }); - return actions. - sendKeys(' memo') - }) - .waitForElementPresent(autoCompleteLineElement('memory')) - .click(autoCompleteLineElement('memory')) - .perform(function () { - const actions = this.actions({ async: true }); - return actions. - sendKeys(' btextbook') - .sendKeys(this.Keys.ENTER) - .sendKeys(', BaseB') - }) - .waitForElementPresent(autoCompleteLineElement('"BaseBook"')) - .click(autoCompleteLineElement('"BaseBook"')) - .perform(function () { - const actions = this.actions({ async: true }); - return actions. - sendKeys(' stor') - }) - .waitForElementPresent(autoCompleteLineElement('storage')) - .click(autoCompleteLineElement('storage')) - .perform(function () { - const actions = this.actions({ async: true }); - return actions. - sendKeys(' localbbook') - }).pause(3000) - }, - 'Should put cursor at the end of function #group2': function (browser: NightwatchBrowser) { - - const path = "//*[@class='view-line' and contains(.,'localbbook') and contains(.,'private')]//span//span[contains(.,'{')]" - browser - .useXpath() - .click(path).pause(1000) - .perform(function () { - const actions = this.actions({ async: true }); - return actions. - // right arrow key - sendKeys(this.Keys.ARROW_RIGHT). - sendKeys(this.Keys.ARROW_RIGHT) - }) - }, - 'Should autcomplete address types': function (browser: NightwatchBrowser) { - browser - .perform(function () { - const actions = this.actions({ async: true }); - return actions. - sendKeys('addre') - }) - .waitForElementPresent(autoCompleteLineElement('address')) - .click(autoCompleteLineElement('address')) - .perform(function () { - const actions = this.actions({ async: true }); - return actions. - sendKeys(' someaddress;') - .sendKeys(this.Keys.ENTER) - }).pause(2000) - .perform(function () { - const actions = this.actions({ async: true }); - return actions. - sendKeys('someaddress.') - }) - .waitForElementVisible(autoCompleteLineElement('balance')) - .waitForElementVisible(autoCompleteLineElement('send')) - .waitForElementVisible(autoCompleteLineElement('transfer')) - .waitForElementVisible(autoCompleteLineElement('code')) - .click(autoCompleteLineElement('balance')) - .perform(function () { - const actions = this.actions({ async: true }); - return actions - .sendKeys(this.Keys.ENTER) - }) - }, - 'Should autcomplete array types': function (browser: NightwatchBrowser) { - browser - .perform(function () { - const actions = this.actions({ async: true }); - return actions. - sendKeys('uin') - }) - .waitForElementPresent(autoCompleteLineElement('uint')) - .click(autoCompleteLineElement('uint')) - .perform(function () { - const actions = this.actions({ async: true }); - return actions. - sendKeys('[] mem') - }) - .waitForElementVisible(autoCompleteLineElement('memory')) - .click(autoCompleteLineElement('memory')) - .perform(function () { - const actions = this.actions({ async: true }); - return actions. - sendKeys(' somearray;') - } - ).pause(2000) - .perform(function () { - const actions = this.actions({ async: true }); - return actions - .sendKeys(this.Keys.ENTER) - .sendKeys('somearray.') - }) - .waitForElementVisible(autoCompleteLineElement('push')) - .waitForElementVisible(autoCompleteLineElement('pop')) - .waitForElementVisible(autoCompleteLineElement('length')) - .click(autoCompleteLineElement('length')) - .perform(function () { - const actions = this.actions({ async: true }); - return actions - .sendKeys(this.Keys.ENTER) - }) - }, - 'Should see and autocomplete second import because it was imported by the first import #group2': function (browser: NightwatchBrowser) { - browser - .perform(function () { - const actions = this.actions({ async: true }); - return actions. - sendKeys('secondi') - }) - .waitForElementPresent(autoCompleteLineElement('secondimport')) - .click(autoCompleteLineElement('secondimport')) - .perform(function () { - const actions = this.actions({ async: true }); - return actions. - sendKeys(' sec;') - .sendKeys(this.Keys.ENTER) - }).pause(3000) - .perform(function () { - const actions = this.actions({ async: true }); - return actions. - sendKeys('sec.') - }) - .waitForElementVisible(autoCompleteLineElement('secondimportstring')) - .click(autoCompleteLineElement('secondimportstring')) - .perform(function () { - const actions = this.actions({ async: true }); - return actions. - sendKeys(';') - .sendKeys(this.Keys.ENTER) - }) - - }, - 'Should see and autocomplete imported local class #group2': function (browser: NightwatchBrowser) { - browser - .perform(function () { - const actions = this.actions({ async: true }); - return actions. - sendKeys('import') - }) - .waitForElementPresent(autoCompleteLineElement('importedcontract')) - .click(autoCompleteLineElement('importedcontract')) - .perform(function () { - const actions = this.actions({ async: true }); - return actions. - sendKeys('.') - }) - .waitForElementVisible(autoCompleteLineElement('externalimport')) - .waitForElementVisible(autoCompleteLineElement('importbasestring')) - .waitForElementVisible(autoCompleteLineElement('importedbook')) - .waitForElementVisible(autoCompleteLineElement('importpublicstring')) - .waitForElementVisible(autoCompleteLineElement('publicimport')) - // no private - .waitForElementNotPresent(autoCompleteLineElement('importprivatestring')) - .waitForElementNotPresent(autoCompleteLineElement('privateimport')) - // no internal - .waitForElementNotPresent(autoCompleteLineElement('importinternalstring')) - .waitForElementNotPresent(autoCompleteLineElement('internalimport')) - .click(autoCompleteLineElement('importbasestring')) - .perform(function () { - const actions = this.actions({ async: true }); - return actions. - sendKeys(';') - .sendKeys(this.Keys.ENTER) - }) - - }, - 'Should autocomplete derived and local event when not using this. #group2': function (browser: NightwatchBrowser) { - browser.perform(function () { - const actions = this.actions({ async: true }); - return actions. - sendKeys('emit base') - }) - .waitForElementVisible(autoCompleteLineElement('BaseEvent')) - .click(autoCompleteLineElement('BaseEvent')) - .perform(function () { - const actions = this.actions({ async: true }); - return actions - .sendKeys('msg.sender') - .sendKeys(this.Keys.TAB) - .sendKeys(this.Keys.TAB) // somehow this is needed to get the cursor to the next parameter, only for selenium - .sendKeys('3232') - .sendKeys(this.Keys.TAB) - .sendKeys(this.Keys.ENTER) - }) - .perform(function () { - const actions = this.actions({ async: true }); - return actions. - sendKeys('emit MyEv') - }) - .waitForElementVisible(autoCompleteLineElement('MyEvent')) - .click(autoCompleteLineElement('MyEvent')) - .perform(function () { - const actions = this.actions({ async: true }); - return actions - .sendKeys('3232') - .sendKeys(this.Keys.TAB) - .sendKeys(this.Keys.ENTER) - }) - }, - - 'Should type and get msg options #group2': function (browser: NightwatchBrowser) { - browser. - perform(function () { - const actions = this.actions({ async: true }); - return actions. - sendKeys(this.Keys.ENTER). - sendKeys('msg.') - }) - .waitForElementVisible(autoCompleteLineElement('sender')) - .waitForElementVisible(autoCompleteLineElement('data')) - .waitForElementVisible(autoCompleteLineElement('value')) - .waitForElementVisible(autoCompleteLineElement('gas')) - .waitForElementVisible(autoCompleteLineElement('sig')) - .click(autoCompleteLineElement('sender')) - .perform(function () { - const actions = this.actions({ async: true }); - return actions. - sendKeys('.') - }) - .waitForElementVisible(autoCompleteLineElement('balance')) - .waitForElementVisible(autoCompleteLineElement('code')) - .waitForElementVisible(autoCompleteLineElement('codehash')) - .waitForElementVisible(autoCompleteLineElement('send')) - .waitForElementVisible(autoCompleteLineElement('transfer')) - .click(autoCompleteLineElement('balance')) - .perform(function () { - const actions = this.actions({ async: true }); - return actions. - sendKeys(this.Keys.ENTER) - }) - }, - 'Should bo and get book #group2': function (browser: NightwatchBrowser) { - browser. - perform(function () { - const actions = this.actions({ async: true }); - return actions. - sendKeys(this.Keys.ENTER). - sendKeys('bo') - }) - .waitForElementVisible(autoCompleteLineElement('book')) - .click(autoCompleteLineElement('book')) - }, - 'Should autcomplete derived struct #group2': function (browser: NightwatchBrowser) { - browser.perform(function () { - const actions = this.actions({ async: true }); - return actions. - sendKeys('.') - }) - .waitForElementVisible(autoCompleteLineElement('author')) - .waitForElementVisible(autoCompleteLineElement('book_id')) - .waitForElementVisible(autoCompleteLineElement('title')) - .click(autoCompleteLineElement('title')) - .perform(function () { - const actions = this.actions({ async: true }); - return actions. - sendKeys(';') - .sendKeys(this.Keys.ENTER) - }) - }, - 'Should bo and get basebook #group2': function (browser: NightwatchBrowser) { - browser. - perform(function () { - const actions = this.actions({ async: true }); - return actions. - sendKeys(this.Keys.ENTER). - sendKeys('base') - }) - .waitForElementVisible(autoCompleteLineElement('basebook')) - .click(autoCompleteLineElement('basebook')) - }, - 'Should autcomplete derived struct from base class #group2': function (browser: NightwatchBrowser) { - browser.perform(function () { - const actions = this.actions({ async: true }); - return actions. - sendKeys('.') - }) - .waitForElementVisible(autoCompleteLineElement('author')) - .waitForElementVisible(autoCompleteLineElement('book_id')) - .waitForElementVisible(autoCompleteLineElement('title')) - .click(autoCompleteLineElement('title')) - .perform(function () { - const actions = this.actions({ async: true }); - return actions. - sendKeys(';') - .sendKeys(this.Keys.ENTER) - }) - }, - 'Should block scoped localbbook #group2': function (browser: NightwatchBrowser) { - browser.pause(4000). - perform(function () { - const actions = this.actions({ async: true }); - return actions. - sendKeys(this.Keys.ENTER). - sendKeys('localb') - }) - .waitForElementVisible(autoCompleteLineElement('localbbook')) - .click(autoCompleteLineElement('localbbook')) - }, - 'Should autcomplete derived struct from block localbbook #group2': function (browser: NightwatchBrowser) { - browser.perform(function () { - const actions = this.actions({ async: true }); - return actions. - sendKeys('.') - }) - .waitForElementVisible(autoCompleteLineElement('author')) - .waitForElementVisible(autoCompleteLineElement('book_id')) - .waitForElementVisible(autoCompleteLineElement('title')) - .click(autoCompleteLineElement('title')) - .perform(function () { - const actions = this.actions({ async: true }); - return actions. - sendKeys(';') - .sendKeys(this.Keys.ENTER) - }) - }, - 'Should block scoped btextbook #group2': function (browser: NightwatchBrowser) { - browser. - perform(function () { - const actions = this.actions({ async: true }); - return actions. - sendKeys(this.Keys.ENTER). - sendKeys('btext') - }) - .waitForElementVisible(autoCompleteLineElement('btextbook')) - .click(autoCompleteLineElement('btextbook')) - }, - 'Should autcomplete derived struct from block btextbook #group2': function (browser: NightwatchBrowser) { - browser.perform(function () { - const actions = this.actions({ async: true }); - return actions. - sendKeys('.') - }) - .waitForElementVisible(autoCompleteLineElement('author')) - .waitForElementVisible(autoCompleteLineElement('book_id')) - .waitForElementVisible(autoCompleteLineElement('title')) - .click(autoCompleteLineElement('title')) - .perform(function () { - const actions = this.actions({ async: true }); - return actions. - sendKeys(';') - .sendKeys(this.Keys.ENTER) - }) - }, - 'Should find private and internal local functions #group2': function (browser: NightwatchBrowser) { - browser.perform(function () { - const actions = this.actions({ async: true }); - return actions. - sendKeys('my') - }) - .waitForElementVisible(autoCompleteLineElement('myprivatefunction')) - .waitForElementVisible(autoCompleteLineElement('myinternalfunction')) - .waitForElementVisible(autoCompleteLineElement('memory')) - .click(autoCompleteLineElement('myinternalfunction')) - .perform(function () { - const actions = this.actions({ async: true }); - return actions. - sendKeys(this.Keys.ENTER) - }) - }, - 'Should find internal functions and var from base and owner #group2': function (browser: NightwatchBrowser) { - browser.perform(function () { - const actions = this.actions({ async: true }); - return actions. - sendKeys('intern') - }) - .waitForElementVisible(autoCompleteLineElement('internalbasefunction')) - .waitForElementVisible(autoCompleteLineElement('internalstring')) - .waitForElementVisible(autoCompleteLineElement('internalbasestring')) - // keyword internal - .waitForElementVisible(autoCompleteLineElement('internal keyword')) - .click(autoCompleteLineElement('internalbasefunction')) - .perform(function () { - const actions = this.actions({ async: true }); - return actions - .sendKeys(this.Keys.ENTER) - }) - }, - - 'Should not find external functions without this. #group2': function (browser: NightwatchBrowser) { - browser.perform(function () { - const actions = this.actions({ async: true }); - return actions. - sendKeys('extern') - }) - .waitForElementNotPresent(autoCompleteLineElement('externalbasefunction')) - .waitForElementNotPresent(autoCompleteLineElement('myexternalfunction')) - // keyword internal - .waitForElementVisible(autoCompleteLineElement('external keyword')) - .perform(function () { - const actions = this.actions({ async: true }); - return actions - .sendKeys(this.Keys.BACK_SPACE) - .sendKeys(this.Keys.BACK_SPACE) - .sendKeys(this.Keys.BACK_SPACE) - .sendKeys(this.Keys.BACK_SPACE) - .sendKeys(this.Keys.BACK_SPACE) - .sendKeys(this.Keys.BACK_SPACE) - .sendKeys(this.Keys.BACK_SPACE) - }) - }, - 'Should find external functions using this. #group2': function (browser: NightwatchBrowser) { - browser. - perform(function () { - const actions = this.actions({ async: true }); - return actions. - sendKeys(this.Keys.ENTER). - sendKeys('this.') - }) - .waitForElementVisible(autoCompleteLineElement('externalbasefunction')) - .waitForElementVisible(autoCompleteLineElement('myexternalfunction')) - }, - 'Should find public functions and vars using this. but not private & other types of nodes #group2': function (browser: NightwatchBrowser) { - browser - .waitForElementVisible(autoCompleteLineElement('"publicbasefunction"')) - .waitForElementVisible(autoCompleteLineElement('"publicstring"')) - .waitForElementVisible(autoCompleteLineElement('"basebook"')) - .waitForElementVisible(autoCompleteLineElement('"mybook"')) - .waitForElementVisible(autoCompleteLineElement('"testing"')) - // but no private functions or vars or other types of nodes - .waitForElementNotPresent(autoCompleteLineElement('"private"')) - .waitForElementNotPresent(autoCompleteLineElement('"BaseEvent"')) - .waitForElementNotPresent(autoCompleteLineElement('"BaseEnum"')) - .waitForElementNotPresent(autoCompleteLineElement('"TestBookDefinition"')) - .click(autoCompleteLineElement('"publicbasefunction"')) - .perform(function () { - const actions = this.actions({ async: true }); - return actions - .sendKeys(this.Keys.ENTER) - }) - }, - 'Should autocomplete local and derived ENUMS #group2': function (browser: NightwatchBrowser) { - browser.perform(function () { - const actions = this.actions({ async: true }); - return actions. - sendKeys('BaseEnum.') - }) - .waitForElementVisible(autoCompleteLineElement('SMALL')) - .waitForElementVisible(autoCompleteLineElement('MEDIUM')) - .waitForElementVisible(autoCompleteLineElement('LARGE')) - .click(autoCompleteLineElement('SMALL')) - .perform(function () { - const actions = this.actions({ async: true }); - return actions. - sendKeys(';') - .sendKeys(this.Keys.ENTER) - .sendKeys('MyEnum.') - }) - .waitForElementVisible(autoCompleteLineElement('SMALL')) - .waitForElementVisible(autoCompleteLineElement('MEDIUM')) - .waitForElementVisible(autoCompleteLineElement('LARGE')) - .click(autoCompleteLineElement('SMALL')) - .perform(function () { - const actions = this.actions({ async: true }); - return actions. - sendKeys(';') - .sendKeys(this.Keys.ENTER) - }) - } -} - From 1e4c047652472960f0f41f52757c9979472d1e68 Mon Sep 17 00:00:00 2001 From: filip mertens Date: Thu, 21 Jul 2022 15:49:43 +0200 Subject: [PATCH 05/10] typo fix --- apps/remix-ide-e2e/src/tests/file_decorator.test.ts | 8 ++++---- .../filedecorationicons/file-decoration-tooltip.tsx | 6 +++--- libs/remix-ui/file-decorators/src/lib/helper/index.tsx | 6 +++--- libs/remix-ui/file-decorators/src/lib/types/index.ts | 2 +- 4 files changed, 11 insertions(+), 11 deletions(-) diff --git a/apps/remix-ide-e2e/src/tests/file_decorator.test.ts b/apps/remix-ide-e2e/src/tests/file_decorator.test.ts index 40e78991b3..2aa226397c 100644 --- a/apps/remix-ide-e2e/src/tests/file_decorator.test.ts +++ b/apps/remix-ide-e2e/src/tests/file_decorator.test.ts @@ -48,7 +48,7 @@ const testScript = ` text: '2', owner: 'code-parser', bubble: true, - commment: 'error on owner', + comment: 'error on owner', } let decorator2: any = { path: 'contracts/2_Owner.sol', @@ -60,7 +60,7 @@ const testScript = ` text: '', owner: 'code-parser', bubble: true, - commment: 'modified', + comment: 'modified', } remix.call('fileDecorator' as any, 'setFileDecorators', [decorator, decorator2]) @@ -74,7 +74,7 @@ const testScript = ` text: '2', owner: 'code-parser', bubble: true, - commment: 'warning on storage', + comment: 'warning on storage', } remix.call('fileDecorator' as any, 'setFileDecorators', decorator) @@ -88,7 +88,7 @@ const testScript = ` text: 'w', owner: 'dgit', bubble: true, - commment: '', + comment: '', } remix.call('fileDecorator' as any, 'setFileDecorators', decorator) diff --git a/libs/remix-ui/file-decorators/src/lib/components/filedecorationicons/file-decoration-tooltip.tsx b/libs/remix-ui/file-decorators/src/lib/components/filedecorationicons/file-decoration-tooltip.tsx index 7c9149c14d..1d87846192 100644 --- a/libs/remix-ui/file-decorators/src/lib/components/filedecorationicons/file-decoration-tooltip.tsx +++ b/libs/remix-ui/file-decorators/src/lib/components/filedecorationicons/file-decoration-tooltip.tsx @@ -9,9 +9,9 @@ const FileDecorationTooltip = (props: { }, ) => { const getComments = function (fileDecoration: fileDecoration) { - if (fileDecoration.commment) { - const commments = Array.isArray(fileDecoration.commment) ? fileDecoration.commment : [fileDecoration.commment] - return commments.map((comment, index) => { + if (fileDecoration.comment) { + const comments = Array.isArray(fileDecoration.comment) ? fileDecoration.comment : [fileDecoration.comment] + return comments.map((comment, index) => { return
{comment}

}) } diff --git a/libs/remix-ui/file-decorators/src/lib/helper/index.tsx b/libs/remix-ui/file-decorators/src/lib/helper/index.tsx index 1afe91b0cf..10bad7d3fc 100644 --- a/libs/remix-ui/file-decorators/src/lib/helper/index.tsx +++ b/libs/remix-ui/file-decorators/src/lib/helper/index.tsx @@ -2,9 +2,9 @@ import React from "react" import { fileDecoration } from "../types" export const getComments = function (fileDecoration: fileDecoration) { - if(fileDecoration.commment){ - const commments = Array.isArray(fileDecoration.commment) ? fileDecoration.commment : [fileDecoration.commment] - return commments.map((comment, index) => { + if(fileDecoration.comment){ + const comments = Array.isArray(fileDecoration.comment) ? fileDecoration.comment : [fileDecoration.comment] + return comments.map((comment, index) => { return
{comment}

}) } diff --git a/libs/remix-ui/file-decorators/src/lib/types/index.ts b/libs/remix-ui/file-decorators/src/lib/types/index.ts index 35178721e0..ef6620434a 100644 --- a/libs/remix-ui/file-decorators/src/lib/types/index.ts +++ b/libs/remix-ui/file-decorators/src/lib/types/index.ts @@ -17,7 +17,7 @@ export enum fileDecorationType { owner: string, workspace?: any tooltip?: string - commment?: string[] | string + comment?: string[] | string } export interface FileType { From 528f53692c064c87cbd7d2e9b812232b9d068374 Mon Sep 17 00:00:00 2001 From: filip mertens Date: Fri, 22 Jul 2022 11:53:33 +0200 Subject: [PATCH 06/10] updates --- .../src/tests/file_decorator.test.ts | 96 ++++++++++++------- .../src/app/plugins/file-decorator.ts | 39 ++++---- .../file-decorators/src/lib/types/index.ts | 2 +- 3 files changed, 86 insertions(+), 51 deletions(-) diff --git a/apps/remix-ide-e2e/src/tests/file_decorator.test.ts b/apps/remix-ide-e2e/src/tests/file_decorator.test.ts index 2aa226397c..9b47bbc61f 100644 --- a/apps/remix-ide-e2e/src/tests/file_decorator.test.ts +++ b/apps/remix-ide-e2e/src/tests/file_decorator.test.ts @@ -11,31 +11,50 @@ module.exports = { 'Test decorators with script': function (browser: NightwatchBrowser) { browser - .openFile('contracts') - .openFile('contracts/2_Owner.sol') - .openFile('contracts/1_Storage.sol') - .openFile('contracts/3_Ballot.sol') - .addFile('scripts/decorators.ts', { content: testScript }) - .pause(2000) - .executeScript('remix.exeCurrent()') - .pause(4000) - .useXpath() - .waitForElementContainsText('//*[@id="fileExplorerView"]//*[@data-id="file-decoration-error-contracts/2_Owner.sol"]', '2') - .waitForElementContainsText('//*[@class="mainview"]//*[@data-id="file-decoration-error-contracts/2_Owner.sol"]', '2') - .waitForElementContainsText('//*[@id="fileExplorerView"]//*[@data-id="file-decoration-custom-contracts/2_Owner.sol"]', 'U') - .waitForElementContainsText('//*[@class="mainview"]//*[@data-id="file-decoration-custom-contracts/2_Owner.sol"]', 'U') - .waitForElementContainsText('//*[@id="fileExplorerView"]//*[@data-id="file-decoration-warning-contracts/1_Storage.sol"]', '2') - .waitForElementContainsText('//*[@class="mainview"]//*[@data-id="file-decoration-warning-contracts/1_Storage.sol"]', '2') - .useCss() - .waitForElementNotPresent('[data-id="file-decoration-custom-contracts/3_Ballot.sol"]', 10000) - .useXpath() - .moveToElement('//*[@id="fileExplorerView"]//*[@data-id="file-decoration-error-contracts/2_Owner.sol"]', 0,0) - .waitForElementVisible('//*[@id="error-tooltip-contracts/2_Owner.sol"]') - .waitForElementContainsText('//*[@id="error-tooltip-contracts/2_Owner.sol"]', 'error on owner') + .openFile('contracts') + .openFile('contracts/2_Owner.sol') + .openFile('contracts/1_Storage.sol') + .openFile('contracts/3_Ballot.sol') + .addFile('scripts/decorators.ts', { content: testScriptSet }) + .pause(2000) + .executeScript('remix.exeCurrent()') + .pause(4000) + .useXpath() + .waitForElementContainsText('//*[@id="fileExplorerView"]//*[@data-id="file-decoration-error-contracts/2_Owner.sol"]', '2') + .waitForElementContainsText('//*[@class="mainview"]//*[@data-id="file-decoration-error-contracts/2_Owner.sol"]', '2') + .waitForElementContainsText('//*[@id="fileExplorerView"]//*[@data-id="file-decoration-custom-contracts/2_Owner.sol"]', 'U') + .waitForElementContainsText('//*[@class="mainview"]//*[@data-id="file-decoration-custom-contracts/2_Owner.sol"]', 'U') + .waitForElementContainsText('//*[@id="fileExplorerView"]//*[@data-id="file-decoration-warning-contracts/1_Storage.sol"]', '2') + .waitForElementContainsText('//*[@class="mainview"]//*[@data-id="file-decoration-warning-contracts/1_Storage.sol"]', '2') + .waitForElementContainsText('//*[@id="fileExplorerView"]//*[@data-id="file-decoration-custom-contracts/3_Ballot.sol"]', 'customtext') + .waitForElementContainsText('//*[@class="mainview"]//*[@data-id="file-decoration-custom-contracts/3_Ballot.sol"]', 'customtext') + .moveToElement('//*[@id="fileExplorerView"]//*[@data-id="file-decoration-error-contracts/2_Owner.sol"]', 0, 0) + .waitForElementVisible('//*[@id="error-tooltip-contracts/2_Owner.sol"]') + .waitForElementContainsText('//*[@id="error-tooltip-contracts/2_Owner.sol"]', 'error on owner') + }, + + 'clear ballot decorator': function (browser: NightwatchBrowser) { + browser + .useCss() + .addFile('scripts/clearballot.ts', { content: testScriptClearBallot }) + .pause(2000) + .executeScript('remix.exeCurrent()') + .pause(4000) + .waitForElementNotPresent('[data-id="file-decoration-custom-contracts/3_Ballot.sol"]', 10000) + }, + 'clear all decorators': function (browser: NightwatchBrowser) { + browser + .addFile('scripts/clearall.ts', { content: testScriptClear }) + .pause(2000) + .executeScript('remix.exeCurrent()') + .pause(4000) + .waitForElementNotPresent('[data-id="file-decoration-error-contracts/2_Owner.sol"]', 10000) + .waitForElementNotPresent('[data-id="file-decoration-warning-contracts/1_Storage.sol"]', 10000) } + } -const testScript = ` +const testScriptSet = ` (async () => { remix.call('fileDecorator' as any, 'clearFileDecorators') let decorator: any = { @@ -46,7 +65,6 @@ const testScript = ` fileStateIconClass: '', fileStateIcon: '', text: '2', - owner: 'code-parser', bubble: true, comment: 'error on owner', } @@ -58,11 +76,10 @@ const testScript = ` fileStateIconClass: 'text-success', fileStateIcon: 'U', text: '', - owner: 'code-parser', bubble: true, comment: 'modified', } - remix.call('fileDecorator' as any, 'setFileDecorators', [decorator, decorator2]) + await remix.call('fileDecorator' as any, 'setFileDecorators', [decorator, decorator2]) decorator = { path: 'contracts/1_Storage.sol', @@ -72,11 +89,10 @@ const testScript = ` fileStateIconClass: '', fileStateIcon: '', text: '2', - owner: 'code-parser', bubble: true, comment: 'warning on storage', } - remix.call('fileDecorator' as any, 'setFileDecorators', decorator) + await remix.call('fileDecorator' as any, 'setFileDecorators', decorator) decorator = { path: 'contracts/3_Ballot.sol', @@ -85,13 +101,25 @@ const testScript = ` fileStateLabelClass: '', fileStateIconClass: '', fileStateIcon: 'customtext', - text: 'w', - owner: 'dgit', + text: 'with text', bubble: true, - comment: '', + comment: 'custom comment', } - remix.call('fileDecorator' as any, 'setFileDecorators', decorator) - - remix.call('fileDecorator' as any, 'clearFileDecorators', 'dgit') + await remix.call('fileDecorator' as any, 'setFileDecorators', decorator) - })()` \ No newline at end of file + })()` + + +const testScriptClearBallot = ` + (async () => { + + await remix.call('fileDecorator' as any, 'clearFileDecorators', 'contracts/3_Ballot.sol') + + })()` + +const testScriptClear = ` + (async () => { + await remix.call('fileDecorator' as any, 'clearAllFileDecorators') + + + })()` \ No newline at end of file diff --git a/apps/remix-ide/src/app/plugins/file-decorator.ts b/apps/remix-ide/src/app/plugins/file-decorator.ts index 5332db1508..5fc495bcf1 100644 --- a/apps/remix-ide/src/app/plugins/file-decorator.ts +++ b/apps/remix-ide/src/app/plugins/file-decorator.ts @@ -8,7 +8,7 @@ import { fileDecoration } from '@remix-ui/file-decorators' const profile = { name: 'fileDecorator', desciption: 'Keeps decorators of the files', - methods: ['setFileDecorators', 'clearFileDecorators'], + methods: ['setFileDecorators', 'clearFileDecorators', 'clearAllFileDecorators'], events: ['fileDecoratorsChanged'], version: '0.0.1' } @@ -25,15 +25,17 @@ export class FileDecorator extends Plugin { * @param fileStates Array of file states */ async setFileDecorators(fileStates: fileDecoration[] | fileDecoration) { + const { from } = this.currentRequest const workspace = await this.call('filePanel', 'getCurrentWorkspace') const fileStatesPayload = Array.isArray(fileStates) ? fileStates : [fileStates] // clear all file states in the previous state of this owner on the files called fileStatesPayload.forEach((state) => { state.workspace = workspace + state.owner = from }) const filteredState = this._fileStates.filter((state) => { const index = fileStatesPayload.findIndex((payloadFileState: fileDecoration) => { - return payloadFileState.owner == state.owner && payloadFileState.path == state.path + return from == state.owner && payloadFileState.path == state.path }) return index == -1 }) @@ -45,21 +47,26 @@ export class FileDecorator extends Plugin { } } - async clearFileDecorators(owner? : string) { - if(!owner) { - this._fileStates = [] - this.emit('fileDecoratorsChanged', []) - } else { - const filteredState = this._fileStates.filter((state) => { - return state.owner != owner - }) - const newState = [...filteredState].sort(sortByPath) - - if (!deepequal(newState, this._fileStates)) { - this._fileStates = newState - this.emit('fileDecoratorsChanged', this._fileStates) - } + async clearFileDecorators(path?: string) { + const { from } = this.currentRequest + if (!from) return + + const filteredState = this._fileStates.filter((state) => { + if(state.owner != from) return true + if(path && state.path != path) return true + }) + const newState = [...filteredState].sort(sortByPath) + + if (!deepequal(newState, this._fileStates)) { + this._fileStates = newState + this.emit('fileDecoratorsChanged', this._fileStates) } + + } + + async clearAllFileDecorators() { + this._fileStates = [] + this.emit('fileDecoratorsChanged', []) } } diff --git a/libs/remix-ui/file-decorators/src/lib/types/index.ts b/libs/remix-ui/file-decorators/src/lib/types/index.ts index ef6620434a..fa783ec6e5 100644 --- a/libs/remix-ui/file-decorators/src/lib/types/index.ts +++ b/libs/remix-ui/file-decorators/src/lib/types/index.ts @@ -14,7 +14,7 @@ export enum fileDecorationType { fileStateIcon: string | HTMLDivElement | JSX.Element, bubble: boolean, text?: string, - owner: string, + owner?: string, workspace?: any tooltip?: string comment?: string[] | string From ead34726c7042af1be35cea7fd4a59f953983921 Mon Sep 17 00:00:00 2001 From: filip mertens Date: Fri, 22 Jul 2022 11:57:25 +0200 Subject: [PATCH 07/10] clear on workspace change --- apps/remix-ide/src/app/plugins/file-decorator.ts | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/apps/remix-ide/src/app/plugins/file-decorator.ts b/apps/remix-ide/src/app/plugins/file-decorator.ts index 5fc495bcf1..c81c1894ff 100644 --- a/apps/remix-ide/src/app/plugins/file-decorator.ts +++ b/apps/remix-ide/src/app/plugins/file-decorator.ts @@ -19,6 +19,11 @@ export class FileDecorator extends Plugin { super(profile) } + onActivation(): void { + this.on('filePanel', 'setWorkspace', async () => { + await this.clearAllFileDecorators() + }) + } /** * From 043069f1120f832e60832269fe0a6e0071a58167 Mon Sep 17 00:00:00 2001 From: bunsenstraat Date: Wed, 3 Aug 2022 12:13:10 +0200 Subject: [PATCH 08/10] fix workspace --- workspace.json | 36 +++++++++--------------------------- 1 file changed, 9 insertions(+), 27 deletions(-) diff --git a/workspace.json b/workspace.json index 95d40e5000..900de9173e 100644 --- a/workspace.json +++ b/workspace.json @@ -1505,35 +1505,16 @@ "builder": "@nrwl/linter:lint", "options": { "linter": "eslint", - "tsConfig": ["libs/remix-ui/file-decorators/tsconfig.lib.json"], - "exclude": ["**/node_modules/**", "!libs/remix-ui/file-decorators/**/*"] + "tsConfig": [ + "libs/remix-ui/file-decorators/tsconfig.lib.json" + ], + "exclude": [ + "**/node_modules/**", + "!libs/remix-ui/file-decorators/**/*" + ] } } } - } - }, - "cli": { - "defaultCollection": "@nrwl/react" - }, - "schematics": { - "@nrwl/workspace": { - "library": { - "linter": "eslint" - } - }, - "@nrwl/react": { - "application": { - "style": "css", - "linter": "eslint", - "babel": true - }, - "component": { - "style": "css" - }, - "library": { - "style": "css", - "linter": "eslint" - } }, "vyper": { "root": "apps/vyper", @@ -1605,7 +1586,8 @@ "linter": "eslint", "config": "apps/vyper/.eslintrc", "files": [ - "apps/vyper/src/**/*.js", "apps/vyper/src/**/*.ts" + "apps/vyper/src/**/*.js", + "apps/vyper/src/**/*.ts" ], "exclude": [ "**/node_modules/**" From 917dd66012cca04c5bd4f7a84097758fe26877d1 Mon Sep 17 00:00:00 2001 From: bunsenstraat Date: Thu, 4 Aug 2022 10:04:06 +0200 Subject: [PATCH 09/10] fix execute --- apps/remix-ide-e2e/src/tests/file_decorator.test.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/apps/remix-ide-e2e/src/tests/file_decorator.test.ts b/apps/remix-ide-e2e/src/tests/file_decorator.test.ts index 9b47bbc61f..3483853cad 100644 --- a/apps/remix-ide-e2e/src/tests/file_decorator.test.ts +++ b/apps/remix-ide-e2e/src/tests/file_decorator.test.ts @@ -17,7 +17,7 @@ module.exports = { .openFile('contracts/3_Ballot.sol') .addFile('scripts/decorators.ts', { content: testScriptSet }) .pause(2000) - .executeScript('remix.exeCurrent()') + .executeScriptInTerminal('remix.exeCurrent()') .pause(4000) .useXpath() .waitForElementContainsText('//*[@id="fileExplorerView"]//*[@data-id="file-decoration-error-contracts/2_Owner.sol"]', '2') @@ -38,7 +38,7 @@ module.exports = { .useCss() .addFile('scripts/clearballot.ts', { content: testScriptClearBallot }) .pause(2000) - .executeScript('remix.exeCurrent()') + .executeScriptInTerminal('remix.exeCurrent()') .pause(4000) .waitForElementNotPresent('[data-id="file-decoration-custom-contracts/3_Ballot.sol"]', 10000) }, @@ -46,7 +46,7 @@ module.exports = { browser .addFile('scripts/clearall.ts', { content: testScriptClear }) .pause(2000) - .executeScript('remix.exeCurrent()') + .executeScriptInTerminal('remix.exeCurrent()') .pause(4000) .waitForElementNotPresent('[data-id="file-decoration-error-contracts/2_Owner.sol"]', 10000) .waitForElementNotPresent('[data-id="file-decoration-warning-contracts/1_Storage.sol"]', 10000) From f915706b2ecd78c7c577939cb6cd05ecae63ac29 Mon Sep 17 00:00:00 2001 From: bunsenstraat Date: Thu, 4 Aug 2022 10:12:08 +0200 Subject: [PATCH 10/10] add filedecorator to required --- apps/remix-ide/src/remixAppManager.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/remix-ide/src/remixAppManager.js b/apps/remix-ide/src/remixAppManager.js index 1bebbc7083..fcf96659ec 100644 --- a/apps/remix-ide/src/remixAppManager.js +++ b/apps/remix-ide/src/remixAppManager.js @@ -11,7 +11,7 @@ const requiredModules = [ // services + layout views + system views 'filePanel', 'terminal', 'settings', 'pluginManager', 'tabs', 'udapp', 'dGitProvider', 'solidity', 'solidity-logic', 'gistHandler', 'layout', 'notification', 'permissionhandler', 'walkthrough', 'storage', 'restorebackupzip', 'link-libraries', 'deploy-libraries', 'openzeppelin-proxy', 'hardhat-provider', 'ganache-provider', 'foundry-provider', 'basic-http-provider', 'injected-optimism-provider', 'injected-arbitrum-one-provider', - 'compileAndRun', 'search', 'recorder'] + 'compileAndRun', 'search', 'recorder', 'fileDecorator'] // dependentModules shouldn't be manually activated (e.g hardhat is activated by remixd) const dependentModules = ['hardhat', 'truffle', 'slither']