import * as vscode from 'vscode' ; import { BehaviorSubject , of , combineLatest } from 'rxjs' ; import { tryShortname } from '../../util' ; import { switchMap , tap } from 'rxjs/operators' ; import Nodes , { Node } from '../../nodes/Nodes' ; const path = require ( 'path' ) ; const fs = require ( 'fs' ) ; export class NodesProvider implements vscode . TreeDataProvider < NodeTreeItem > { nodeTreeItems : NodeTreeItem [ ] = [ ] ; private _onDidChangeTreeData : vscode . EventEmitter < NodeTreeItem | undefined > = new vscode . EventEmitter < NodeTreeItem | undefined > ( ) ; readonly onDidChangeTreeData : vscode . Event < NodeTreeItem | undefined > = this . _onDidChangeTreeData . event ; constructor ( nodes , selectedNode$ ) { nodes . nodes$ . subscribe ( ( nodes ) => { this . nodeTreeItems = nodes . map ( ( node ) => { return new NodeTreeItem ( node ) ; } ) ; this . changeSelected ( selectedNode$ . getValue ( ) ?. nodePath || null ) ; this . _onDidChangeTreeData . fire ( ) ; } ) ; selectedNode$ . subscribe ( node => { this . changeSelected ( node ?. nodePath || null ) ; } ) ; } changeSelected ( selectedNodePath ) { this . nodeTreeItems . forEach ( ( nodeTreeItem ) => { if ( nodeTreeItem . nodePath === selectedNodePath ) nodeTreeItem . select ( ) ; else nodeTreeItem . unselect ( ) ; } ) ; this . _onDidChangeTreeData . fire ( ) ; } getTreeItem ( element ) { return element ; } getChildren ( element ? ) { if ( element === undefined ) { return this . nodeTreeItems ; } return element . children ; } } export class NodeTreeItem extends vscode . TreeItem { children : undefined ; nodePath : string ; static create ( info ) { return new this ( info ) ; } constructor ( node ) { const { nodePath } = node ; super ( tryShortname ( nodePath ) , vscode . TreeItemCollapsibleState . None ) ; this . nodePath = nodePath ; this . command = { command : "substrateNodes.selectNode" , title : "Select Node" , arguments : [ this ] } ; } select ( ) { this . label = '▶️ ' + tryShortname ( this . nodePath ) ; } unselect ( ) { this . label = tryShortname ( this . nodePath ) ; } } async function quickPickNodePath ( nodes ) { let nodePaths = nodes . nodes$ . getValue ( ) . map ( ( node ) => node . nodePath ) ; if ( nodePaths . length === 1 ) return nodePaths [ 0 ] ; if ( nodePaths . length === 0 ) { vscode . window . showErrorMessage ( 'No node was found in the workspace.' ) ; return Promise . reject ( ) ; } const nodesReadable = nodePaths . map ( n => tryShortname ( n ) ) ; const pick = await vscode . window . showQuickPick ( nodesReadable , { placeHolder : "Please choose a node." } ) ; if ( pick === undefined ) return Promise . reject ( ) ; return nodePaths [ nodesReadable . findIndex ( x => x === pick ) ] ; } function getCargoName ( directory ) { const toml = fs . readFileSync ( path . join ( directory , 'Cargo.toml' ) ) . toString ( ) ; const ap = toml . match ( /name ?= ?'(.+)'/ ) ; if ( ap ) return ap [ 1 ] ; else return toml . match ( /name ?= ?"(.+)"/ ) [ 1 ] ; } function getWorkspaceRoot ( directory ) { let currDir = directory ; do { if ( fs . existsSync ( path . join ( currDir , 'Cargo.lock' ) ) ) return currDir ; currDir = path . join ( currDir , '..' ) ; } while ( currDir . split ( path . sep ) . length > 0 ) ; throw new Error ( 'No workspace root was found' ) ; } export function setUpNodesTreeView ( nodes , processes ) { vscode . commands . registerCommand ( "substrate.startNode" , async ( nodePathLike ? , _flags ? , options = { compile : false } ) => { try { let nodePath = nodePathLike instanceof NodeTreeItem ? nodePathLike . nodePath : nodePathLike ; if ( nodePath ) { selectedNodePath$ . next ( nodePath ) ; } const defNodePath = nodePath || await quickPickNodePath ( nodes ) ; const term = vscode . window . createTerminal ( { name : 'Start node ' + tryShortname ( defNodePath ) , cwd : defNodePath } ) ; const flags = _flags ? ( Array . isArray ( _flags ) ? _flags . join ( ' ' ) : _flags ) : await vscode . window . showInputBox ( { value : '--dev --ws-external' , prompt : 'Flags to run Substrate with' , ignoreFocusOut : true } ) ; if ( ! flags ) return ; if ( options . compile ) { term . sendText ( `cargo run --release -- ${ flags }` ) ; } else { term . sendText ( path . join ( getWorkspaceRoot ( defNodePath ) , `target/release/${ getCargoName ( defNodePath ) }` ) + ' ' + flags ) ; } term . show ( ) ; processes . new ( { nodePath : defNodePath , command : flags , term : term } ) ; } catch ( e ) { vscode . window . showErrorMessage ( e ) ; console . error ( e ) ; } } ) ; vscode . commands . registerCommand ( "substrate.compileStartNode" , async ( nodePathLike ? , _flags ? ) => { vscode . commands . executeCommand ( "substrate.startNode" , nodePathLike , _flags , { compile : true } ) ; } ) ; vscode . commands . registerCommand ( "substrate.purgeChain" , async ( nodePathLike ? ) => { let nodePath = nodePathLike instanceof NodeTreeItem ? nodePathLike . nodePath : nodePathLike ; if ( nodePath ) { selectedNodePath$ . next ( nodePath ) ; } const defoNodePath = nodePath || await quickPickNodePath ( nodes ) ; const term = vscode . window . createTerminal ( { name : 'Purge chain' , cwd : defoNodePath } ) ; term . sendText ( path . join ( getWorkspaceRoot ( defoNodePath ) , `target/release/${ getCargoName ( defoNodePath ) }` ) + ' purge-chain --dev' ) ; term . show ( ) ; } ) ; const selectedNodePath$ = new BehaviorSubject < string | null > ( null ) ; const selectedNode$ = new BehaviorSubject < Node | null > ( null ) ; combineLatest ( selectedNodePath$ , nodes . nodes$ ) . pipe ( switchMap ( ( [ nodePath , nodes ] ) => { if ( ! nodePath ) return of ( null ) ; const selectedNode = nodes . find ( node => node . nodePath === nodePath ) ; if ( selectedNode === undefined ) { console . error ( "Selected node but doesn't match any." ) ; return of ( null ) ; } return of ( selectedNode ) ; } ) ) . subscribe ( selectedNode$ ) ; const treeDataProvider = new NodesProvider ( nodes , selectedNode$ ) ; vscode . window . createTreeView ( 'substrateNodes' , { treeDataProvider } ) ; vscode . commands . registerCommand ( "substrateNodes.selectNode" , ( item ) => { selectedNodePath$ . next ( ( item as any ) . nodePath || null ) ; } ) ; return { selectedNode$ } ; }