diff --git a/apps/remix-ide/src/app/panels/file-panel.js b/apps/remix-ide/src/app/panels/file-panel.js index 2ff7a1da01..326d776e14 100644 --- a/apps/remix-ide/src/app/panels/file-panel.js +++ b/apps/remix-ide/src/app/panels/file-panel.js @@ -64,6 +64,16 @@ module.exports = class Filepanel extends ViewPlugin { return
} + compileContractForUml (path) { + console.log({ path }) + this.call('solidity', 'compile', async (target, source) => { + console.log({ + target, + source + }) + }) + } + /** * @param item { id: string, name: string, type?: string[], path?: string[], extension?: string[], pattern?: string[] } * @param callback (...args) => void diff --git a/libs/remix-ui/solidity-compiler/src/lib/contract-selection.tsx b/libs/remix-ui/solidity-compiler/src/lib/contract-selection.tsx index fed0559aa7..b4ab7dba69 100644 --- a/libs/remix-ui/solidity-compiler/src/lib/contract-selection.tsx +++ b/libs/remix-ui/solidity-compiler/src/lib/contract-selection.tsx @@ -23,6 +23,7 @@ export const ContractSelection = (props: ContractSelectionProps) => { const [svgPayload, setSVGPayload] = useState('') const [showViewer, setShowViewer] = useState(false) const [contentForAST, setContentForAST] = useState('') + console.log({ props }) const intl = useIntl() diff --git a/libs/remix-ui/workspace/src/lib/components/file-explorer-context-menu.tsx b/libs/remix-ui/workspace/src/lib/components/file-explorer-context-menu.tsx index 66432f6872..7422e83a7d 100644 --- a/libs/remix-ui/workspace/src/lib/components/file-explorer-context-menu.tsx +++ b/libs/remix-ui/workspace/src/lib/components/file-explorer-context-menu.tsx @@ -13,7 +13,7 @@ declare global { const _paq = window._paq = window._paq || [] //eslint-disable-line export const FileExplorerContextMenu = (props: FileExplorerContextMenuProps) => { - const { actions, createNewFile, createNewFolder, deletePath, renamePath, hideContextMenu, pushChangesToGist, publishFileToGist, publishFolderToGist, copy, copyFileName, copyPath, paste, runScript, emit, pageX, pageY, path, type, focus, ...otherProps } = props + const { actions, createNewFile, createNewFolder, deletePath, renamePath, hideContextMenu, pushChangesToGist, publishFileToGist, publishFolderToGist, copy, copyFileName, copyPath, paste, runScript, emit, pageX, pageY, path, type, focus, generateUml, ...otherProps } = props const contextMenuRef = useRef(null) const intl = useIntl() useEffect(() => { @@ -64,7 +64,9 @@ export const FileExplorerContextMenu = (props: FileExplorerContextMenuProps) => } const menu = () => { - return actions.filter(item => filterItem(item)).map((item, index) => { + return actions.filter(item => filterItem(item)).sort((a, b) => { + return a.name > b.name ? -1 : a.name < b.name ? 1 : 0 + }).reverse().map((item, index) => { return
  • deletePath(getPath()) _paq.push(['trackEvent', 'fileExplorer', 'contextMenu', 'deleteAll']) break + case 'Generate uml diagram': + generateUml(path) + break default: _paq.push(['trackEvent', 'fileExplorer', 'customAction', `${item.id}/${item.name}`]) emit && emit({ ...item, path: [path] } as customAction) 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 3ffcf6abb3..fac419327f 100644 --- a/libs/remix-ui/workspace/src/lib/components/file-explorer.tsx +++ b/libs/remix-ui/workspace/src/lib/components/file-explorer.tsx @@ -5,6 +5,14 @@ import { FileExplorerContextMenu } from './file-explorer-context-menu' // eslint import { FileExplorerProps, MenuItems, FileExplorerState } from '../types' import { customAction } from '@remixproject/plugin-api/lib/file-system/file-panel' import { contextMenuActions } from '../utils' +const parser = (window as any).SolidityParser +import { convertUmlClasses2Dot } from 'sol2uml/lib/converterClasses2Dot' +import vizRenderStringSync from '@aduh95/viz.js/sync' +import domToPdf from 'dom-to-pdf' +import { jsPDF } from 'jspdf' +import { convertAST2UmlClasses } from 'sol2uml' +import { createClient } from '@remixproject/plugin-webview' +import { PluginClient } from '@remixproject/plugin' import '../css/file-explorer.css' import { checkSpecialChars, extractNameFromKey, extractParentFromKey, joinPath } from '@remix-ui/helper' @@ -12,9 +20,11 @@ import { checkSpecialChars, extractNameFromKey, extractParentFromKey, joinPath } import { FileRender } from './file-render' import { Drag } from "@remix-ui/drag-n-drop" import { ROOT_PATH } from '../utils/constants' +import { IRemixApi } from '@remixproject/plugin-api' + export const FileExplorer = (props: FileExplorerProps) => { - const { name, contextMenuItems, removedContextMenuItems, files, fileState } = props + const { name, contextMenuItems, removedContextMenuItems, files, fileState, plugin } = props const [state, setState] = useState({ ctrlKey: false, newFileName: '', @@ -38,7 +48,13 @@ export const FileExplorer = (props: FileExplorerProps) => { }) const [canPaste, setCanPaste] = useState(false) const treeRef = useRef(null) + const [client, setClient] = useState(null) + const [isClientLoaded, setIsClientLoaded] = useState(false); + const [svgPayload, setSVGPayload] = useState('') + const [showViewer, setShowViewer] = useState(false) + const [contentForAST, setContentForAST] = useState('') + useEffect(() => { if (contextMenuItems) { addMenuItems(contextMenuItems) @@ -435,6 +451,59 @@ export const FileExplorer = (props: FileExplorerProps) => { props.modal('Moving Folder Failed', 'Unexpected error while moving folder: ' + src, 'Close', async () => {}) } } + + /** + * Take AST and generates a UML diagram of compiled contract as svg + * @returns void + */ + const generateUml = (path: string) => { + try { + const currentFile = path + let ast: any + plugin.compileContractForUml(path) + const client: PluginClient> = new PluginClient() + client.call('solidity', '') + plugin.call('solidity', '') + // client.call('solidity', 'compile', path) + // client.on('solidity', 'compilationFinished', async (target, compileSource) => { + // ast = parser.parse(compileSource.sources[currentFile].content) + // console.log({ ast }) + // }) + // contentForAST.length > 1 ? parser.parse(contentForAST) : + // const payload = vizRenderStringSync(convertUmlClasses2Dot(convertAST2UmlClasses(ast, currentFile))) + // const fileName = `${currentFile.split('/')[0]}/resources/${currentFile.split('/')[1].split('.')[0]}.pdf` + + // const element = new DOMParser().parseFromString(payload, 'image/svg+xml') + // .querySelector('svg') + // domToPdf(element, { filename: `${currentFile.split('/')[1].split('.')[0]}.pdf`, scale: 1.2 }, (pdf: jsPDF) => { + + // // api.writeFile(fileName, pdf.output()) + // }) + // setSVGPayload(payload) + // setShowViewer(!showViewer) + } catch (error) { + console.log({ error }) + } + } + + /** + * Takes currently compiled contract that has a bunch of imports at the top + * and flattens them ready for UML creation. Takes the flattened result + * and assigns to a local property + * @returns void + */ + // const flattenContract = () => { + // const filePath = api.getCompilationResult().source.target + // const ast = api.getCompilationResult().data.sources + // const dependencyGraph = getDependencyGraph(ast, filePath) + // const sorted = dependencyGraph.isEmpty() + // ? [filePath] + // : dependencyGraph.sort().reverse() + // const sources = api.getCompilationResult().source.sources + // const result = concatSourceFiles(sorted, sources) + // api.writeFile(`${api.currentFile}_flattened.sol`, result) + // setContentForAST(result) + // } return (
    @@ -502,6 +571,7 @@ export const FileExplorer = (props: FileExplorerProps) => { pushChangesToGist={pushChangesToGist} publishFolderToGist={publishFolderToGist} publishFileToGist={publishFileToGist} + generateUml={generateUml} /> }
    diff --git a/libs/remix-ui/workspace/src/lib/contexts/index.ts b/libs/remix-ui/workspace/src/lib/contexts/index.ts index 5627acdf0a..d6160c4c9e 100644 --- a/libs/remix-ui/workspace/src/lib/contexts/index.ts +++ b/libs/remix-ui/workspace/src/lib/contexts/index.ts @@ -1,10 +1,13 @@ import { customAction } from '@remixproject/plugin-api/lib/file-system/file-panel/type' import { createContext, SyntheticEvent } from 'react' import { BrowserState } from '../reducers/workspace' +import { FilePanelType } from '../types' + export const FileSystemContext = createContext<{ fs: BrowserState, modal:(title: string | JSX.Element, message: string | JSX.Element, okLabel: string, okFn: () => void, cancelLabel?: string, cancelFn?: () => void) => void, + plugin: FilePanelType dispatchInitWorkspace:() => Promise, dispatchFetchDirectory:(path: string) => Promise, dispatchAddInputField:(path: string, type: 'file' | 'folder') => Promise, diff --git a/libs/remix-ui/workspace/src/lib/providers/FileSystemProvider.tsx b/libs/remix-ui/workspace/src/lib/providers/FileSystemProvider.tsx index ffcd77abaa..144c7735d8 100644 --- a/libs/remix-ui/workspace/src/lib/providers/FileSystemProvider.tsx +++ b/libs/remix-ui/workspace/src/lib/providers/FileSystemProvider.tsx @@ -245,6 +245,7 @@ export const FileSystemProvider = (props: WorkspaceProps) => { fs, modal, toast, + plugin, dispatchInitWorkspace, dispatchFetchDirectory, dispatchAddInputField, 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 6eba79f344..211bf9dfbd 100644 --- a/libs/remix-ui/workspace/src/lib/remix-ui-workspace.tsx +++ b/libs/remix-ui/workspace/src/lib/remix-ui-workspace.tsx @@ -35,6 +35,7 @@ export function Workspace () { const initGitRepoRef = useRef() const filteredBranches = selectedWorkspace ? (selectedWorkspace.branches || []).filter(branch => branch.name.includes(branchFilter) && branch.name !== 'HEAD').slice(0, 20) : [] const currentBranch = selectedWorkspace ? selectedWorkspace.currentBranch : null + const plugin = global.plugin useEffect(() => { let workspaceName = localStorage.getItem('currentWorkspace') @@ -498,6 +499,7 @@ export function Workspace () { removedContextMenuItems={global.fs.browser.contextMenu.removedMenuItems} files={global.fs.browser.files} fileState={global.fs.browser.fileState} + plugin={plugin} expandPath={global.fs.browser.expandPath} focusEdit={global.fs.focusEdit} focusElement={global.fs.focusElement} @@ -537,6 +539,7 @@ export function Workspace () { removedContextMenuItems={global.fs.localhost.contextMenu.removedMenuItems} files={global.fs.localhost.files} fileState={[]} + plugin={plugin} 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 07ccce4cb5..22b33587f9 100644 --- a/libs/remix-ui/workspace/src/lib/types/index.ts +++ b/libs/remix-ui/workspace/src/lib/types/index.ts @@ -1,6 +1,8 @@ import React from 'react' import { customAction } from '@remixproject/plugin-api/lib/file-system/file-panel' import { fileDecoration } from '@remix-ui/file-decorators'; +import { IRemixApi } from '@remixproject/plugin-api'; +import { MethodParams } from '@remixproject/plugin-utils'; 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 { @@ -16,32 +18,7 @@ export interface JSONStandardInput { export type MenuItems = action[] export type WorkspaceTemplate = 'gist-template' | 'code-template' | 'remixDefault' | 'blank' | 'ozerc20' | 'zeroxErc20' | 'ozerc721' export interface WorkspaceProps { - plugin: { - setWorkspace: ({ name, isLocalhost }, setEvent: boolean) => void, - createWorkspace: (name: string, workspaceTemplateName: string) => void, - renameWorkspace: (oldName: string, newName: string) => void - workspaceRenamed: ({ name }) => void, - workspaceCreated: ({ name }) => void, - workspaceDeleted: ({ name }) => void, - workspace: any // workspace provider, - browser: any // browser provider - localhost: any // localhost provider - fileManager : any - registry: any // registry - request: { - createWorkspace: () => void, - setWorkspace: (workspaceName: string) => void, - createNewFile: () => void, - uploadFile: (target: EventTarget & HTMLInputElement) => void, - getCurrentWorkspace: () => void - } // api request, - workspaces: any, - registeredMenuItems: MenuItems // menu items - removedMenuItems: MenuItems - initialWorkspace: string, - resetNewFile: () => void, - getWorkspaces: () => string[] - } + plugin: FilePanelType } export interface WorkspaceState { hideRemixdExplorer: boolean @@ -67,10 +44,41 @@ export interface FileType { child?: File[] } +export type FilePanelType = { + , Key extends MethodKey>(name: Name, key: Key, ...payload: MethodParams) + setWorkspace: ({ name, isLocalhost }, setEvent: boolean) => void, + createWorkspace: (name: string, workspaceTemplateName: string) => void, + renameWorkspace: (oldName: string, newName: string) => void + compileContractForUml: (path: string) => void + workspaceRenamed: ({ name }) => void, + workspaceCreated: ({ name }) => void, + workspaceDeleted: ({ name }) => void, + workspace?: any // workspace provider, + browser?: any // browser provider + localhost?: any // localhost provider + fileManager? : any + registry?: any // registry + pluginApi?: any + request: { + createWorkspace: () => void, + setWorkspace: (workspaceName: string) => void, + createNewFile: () => void, + uploadFile: (target: EventTarget & HTMLInputElement) => void, + getCurrentWorkspace: () => void + } // api request, + workspaces: any, + registeredMenuItems: MenuItems // menu items + removedMenuItems: MenuItems + initialWorkspace: string, + resetNewFile: () => void, + getWorkspaces: () => string[] + } + /* eslint-disable-next-line */ export interface FileExplorerProps { name: string, menuItems?: string[], + plugin: FilePanelType contextMenuItems: MenuItems, removedContextMenuItems: MenuItems, files: { [x: string]: Record }, @@ -136,6 +144,7 @@ export interface FileExplorerContextMenuProps { paste?: (destination: string, type: string) => void copyFileName?: (path: string, type: string) => void copyPath?: (path: string, type: string) => void + generateUml?: (path: string) => void } export interface FileExplorerState { diff --git a/libs/remix-ui/workspace/src/lib/utils/index.ts b/libs/remix-ui/workspace/src/lib/utils/index.ts index 8a0762ae7f..20797a02ae 100644 --- a/libs/remix-ui/workspace/src/lib/utils/index.ts +++ b/libs/remix-ui/workspace/src/lib/utils/index.ts @@ -30,6 +30,13 @@ export const contextMenuActions: MenuItems = [{ extension: ['.js', '.ts'], multiselect: false, label: '' +}, +{ + id: 'generate', + name: 'Generate uml diagram', + extension: ['.sol'], + multiselect: false, + label: '' }, { id: 'pushChangesToGist', name: 'Push changes to gist',