import * as vscode from 'vscode' ; import { BehaviorSubject } from 'rxjs' ; import fetchCategories from './fetchCategories' ; import { substrateDepsInstalled } from './substrateDeps' ; import { Category , Pallet } from './types' ; import Nodes , { Node } from '../../nodes/Nodes' ; import { tryShortname } from '../../util' ; const path = require ( 'path' ) ; type TreeItem = TreePallet | TreeCategory ; export class TreeDataProvider implements vscode . TreeDataProvider < TreeItem > { treeCategories : TreeCategory [ ] ; private _selectedNode$ : any ; private _onDidChangeTreeData : vscode . EventEmitter < TreeCategory | undefined > = new vscode . EventEmitter < TreeCategory | undefined > ( ) ; readonly onDidChangeTreeData : vscode . Event < TreeCategory | undefined > = this . _onDidChangeTreeData . event ; constructor ( data , selectedNode$ ) { this . treeCategories = data . map ( ( category ) => { return new TreeCategory ( category , category . pallets . map ( TreePallet . create . bind ( TreePallet ) ) ) ; } ) ; this . _selectedNode$ = selectedNode$ ; selectedNode$ . subscribe ( ( node ) => { this . treeCategories . forEach ( treeCategory => { treeCategory . children ?. forEach ( treePallet => { treePallet . contextValue = node && node . deps && node . deps . includes ( treePallet . name ) ? 'palletInstalled' : node ? 'pallet' : 'palletNoNode' ; treePallet . iconPath = treePallet . contextValue === 'palletInstalled' ? path . join ( __filename , '..' , '..' , '..' , '..' , 'resources' , 'check.svg' ) : false ; } ) ; } ) ; this . _onDidChangeTreeData . fire ( ) ; } ) ; } getTreeItem ( element ) { return element ; } getChildren ( element ? ) { if ( element === undefined ) { return this . treeCategories ; } return element . children ; } } export class TreeCategory extends vscode . TreeItem { children : TreePallet [ ] | undefined ; constructor ( info , children ? ) { const { category : label } = info ; super ( label , children === undefined ? vscode . TreeItemCollapsibleState . None : vscode . TreeItemCollapsibleState . Expanded ) ; this . children = children ; this . contextValue = 'category' ; } } export class TreePallet extends vscode . TreeItem { children : TreeItem [ ] | undefined ; name : string ; github : string ; documentation : string ; homepage : string ; static create ( info ) { return new this ( info ) ; } constructor ( info ) { const { name , description , github , documentation , homepage } = info ; super ( name , vscode . TreeItemCollapsibleState . None ) ; this . name = name ; this . description = description ; this . tooltip = `${ name } - ${ description }` ; this . contextValue = 'palletNoNode' ; this . github = github ; this . documentation = documentation ; this . homepage = homepage ; } } type NodePath = string | null ; export function setUpMarketplaceTreeView ( nodes , selectedNode$ ) { fetchCategories ( ) . then ( ( categories ) => { const treeView = vscode . window . createTreeView ( 'substrateMarketplace' , { treeDataProvider : new TreeDataProvider ( categories , selectedNode$ ) } ) ; selectedNode$ . subscribe ( ( node ) => { if ( node && node . runtimePath ) treeView . message = `Runtime: ${ tryShortname ( node . runtimePath ) }` ; else treeView . message = `No node selected` ; } ) ; ( [ { command : 'substrateMarketplace.palletDocumentation' , name : 'Documentation' , property : 'documentation' } , { command : 'substrateMarketplace.palletGithub' , name : 'GitHub page' , property : 'github' } , { command : 'substrateMarketplace.palletHomepage' , name : 'Homepage' , property : 'homepage' } ] as const ) . forEach ( ( { command , name , property } ) => { vscode . commands . registerCommand ( command , ( item ) => { if ( ! item [ property ] . startsWith ( 'http' ) ) { vscode . window . showErrorMessage ( `${ name } is unavailable for this pallet.` ) ; return ; } vscode . commands . executeCommand ( 'vscode.open' , vscode . Uri . parse ( item [ property ] ) ) ; } ) ; } ) ; vscode . commands . registerCommand ( "substrateMarketplace.installPallet" , async ( item ) => { if ( ! await substrateDepsInstalled ( ) ) { return ; } const palletName = item . label as string ; if ( ! /^[a-z-]+$/ . test ( palletName ) ) { vscode . window . showErrorMessage ( 'Pallet name is invalid.' ) ; return ; } const alias = ( alias => alias === palletName ? null : alias ) ( palletName . replace ( /^pallet-/ , '' ) ) ; const clicked = await vscode . window . showInformationMessage ( `Install the pallet ${ palletName }?` , { modal : true } , 'Yes' ) ; if ( clicked !== 'Yes' ) { return ; } let manifestPath ; try { let selectedNodeRuntimePath = selectedNode$ . getValue ( ) ?. runtimePath ; if ( ! selectedNodeRuntimePath ) { vscode . window . showErrorMessage ( 'Please first select a node.' ) ; return ; } manifestPath = path . join ( selectedNodeRuntimePath , 'Cargo.toml' ) ; } catch ( e ) { return ; } const termCommand = [ 'substrate-deps' , `add ${ palletName }` , ... alias ? [ `--alias ${ alias }` ] : [ ] , `--manifest-path '${ manifestPath . replace ( /'/ , `'\\''` ) }'` , `&& echo '${ palletName } was successfully added to the project${ alias ? ` as '${ alias }'` : '' }.'` ] . join ( ' ' ) ; const term = vscode . window . createTerminal ( { name : `Installing ${ palletName }` } ) ; term . sendText ( termCommand ) ; term . show ( ) ; } ) ; } , ( async ( r ) => { const clicked = await vscode . window . showErrorMessage ( `An error occured when fetching the list of pallets from the Substrate Marketplace: ${ r }` , 'Try again' ) ; if ( clicked === 'Try again' ) { return setUpMarketplaceTreeView ( nodes , selectedNode$ ) ; } } ) ) ; }