import * as vscode from 'vscode' ; import { JestExt } from './JestExt' ; import { DebugCodeLensProvider , TestState } from './DebugCodeLens' ; import { DebugConfigurationProvider } from './DebugConfigurationProvider' ; import { PluginWindowSettings } from './Settings' ; import { statusBar } from './StatusBar' ; import { CoverageCodeLensProvider } from './Coverage' ; import { extensionName } from './appGlobals' ; export type GetJestExtByURI = ( uri : vscode . Uri ) => JestExt | undefined ; export function getExtensionWindowSettings ( ) { const config = vscode . workspace . getConfiguration ( 'jest' ) ; return { debugCodeLens : { enabled : config . get < boolean > ( 'enableCodeLens' ) ! , showWhenTestStateIn : config . get < TestState [ ] > ( 'debugCodeLens.showWhenTestStateIn' ) ! , } , enableSnapshotPreviews : config . get < boolean > ( 'enableSnapshotPreviews' ) , disabledWorkspaceFolders : config . get < string [ ] > ( 'disabledWorkspaceFolders' ) ! , } ; } export type RegisterCommand = { type : 'all-workspaces' ; name : string ; callback : ( extension : JestExt , ... args : any [ ] ) => any ; } | { type : 'select-workspace' ; name : string ; callback : ( extension : JestExt , ... args : any [ ] ) => any ; } | { type : 'active-text-editor' | 'active-text-editor-workspace' ; name : string ; callback : ( extension : JestExt , textEditor : vscode . TextEditor , ... args : any [ ] ) => any ; } ; type CommandType = RegisterCommand [ 'type' ] ; const CommandPrefix = { 'all-workspaces' : `${ extensionName }` , 'select-workspace' : `${ extensionName }.workspace` , 'active-text-editor' : `${ extensionName }.editor` , 'active-text-editor-workspace' : `${ extensionName }.editor.workspace` , } ; export class ExtensionManager { debugCodeLensProvider : DebugCodeLensProvider ; debugConfigurationProvider : DebugConfigurationProvider ; coverageCodeLensProvider : CoverageCodeLensProvider ; private extByWorkspace : Map < string , JestExt > = new Map ( ) ; private context : vscode . ExtensionContext ; private commonPluginSettings : PluginWindowSettings ; constructor ( context ) { this . context = context ; this . commonPluginSettings = getExtensionWindowSettings ( ) ; this . debugConfigurationProvider = new DebugConfigurationProvider ( ) ; this . debugCodeLensProvider = new DebugCodeLensProvider ( this . getByDocUri ) ; this . coverageCodeLensProvider = new CoverageCodeLensProvider ( this . getByDocUri ) ; this . applySettings ( getExtensionWindowSettings ( ) ) ; } applySettings ( settings ) { this . commonPluginSettings = settings ; const { debugCodeLens } = settings ; this . debugCodeLensProvider . showWhenTestStateIn = debugCodeLens . enabled ? debugCodeLens . showWhenTestStateIn : [ ] ; settings . disabledWorkspaceFolders . forEach ( this . unregisterByName , this ) ; vscode . workspace . workspaceFolders ?. forEach ( ( ws ) => { if ( ! this . extByWorkspace . get ( ws . name ) ) { this . register ( ws ) ; } } ) ; } register ( workspaceFolder ) { if ( ! this . shouldStart ( workspaceFolder . name ) ) { return ; } const jestExt = new JestExt ( this . context , workspaceFolder , this . debugCodeLensProvider , this . debugConfigurationProvider , this . coverageCodeLensProvider ) ; this . extByWorkspace . set ( workspaceFolder . name , jestExt ) ; jestExt . startSession ( ) ; } unregister ( workspaceFolder ) { this . unregisterByName ( workspaceFolder . name ) ; } unregisterByName ( name ) { const extension = this . extByWorkspace . get ( name ) ; if ( extension ) { extension . deactivate ( ) ; this . extByWorkspace . delete ( name ) ; } } unregisterAll ( ) { const keys = this . extByWorkspace . keys ( ) ; for ( const key of keys ) { this . unregisterByName ( key ) ; } } shouldStart ( workspaceFolderName ) { const { commonPluginSettings : { disabledWorkspaceFolders } , } = this ; if ( this . extByWorkspace . has ( workspaceFolderName ) ) { return false ; } if ( disabledWorkspaceFolders . includes ( workspaceFolderName ) ) { return false ; } return true ; } public getByName = ( workspaceFolderName ) : JestExt | undefined => { return this . extByWorkspace . get ( workspaceFolderName ) ; } ; public getByDocUri : GetJestExtByURI = ( uri ) => { const workspace = vscode . workspace . getWorkspaceFolder ( uri ) ; if ( workspace ) { return this . getByName ( workspace . name ) ; } } ; async selectExtension ( ) { const workspace = vscode . workspace . workspaceFolders && vscode . workspace . workspaceFolders . length <= 1 ? vscode . workspace . workspaceFolders [ 0 ] : await vscode . window . showWorkspaceFolderPick ( ) ; const instance = workspace && this . getByName ( workspace . name ) ; if ( instance ) { return instance ; } else if ( workspace ) { throw new Error ( `No Jest instance in ${ workspace . name } workspace` ) ; } } param command param callback param thisArg registerCommand ( command , thisArg ? ) { const commandName = `${ CommandPrefix [ command . type ] }.${ command . name }` ; switch ( command . type ) { case 'all-workspaces' : { return vscode . commands . registerCommand ( commandName , async ( ... args ) => { vscode . workspace . workspaceFolders ?. forEach ( ( ws ) => { const extension = this . getByName ( ws . name ) ; if ( extension ) { command . callback . call ( thisArg , extension , ... args ) ; } } ) ; } ) ; } case 'select-workspace' : { return vscode . commands . registerCommand ( commandName , async ( ... args ) => { const extension = await this . selectExtension ( ) ; if ( extension ) { command . callback . call ( thisArg , extension , ... args ) ; } } ) ; } case 'active-text-editor' : case 'active-text-editor-workspace' : { return vscode . commands . registerTextEditorCommand ( commandName , ( editor , _edit , ... args ) => { const workspace = vscode . workspace . getWorkspaceFolder ( editor . document . uri ) ; if ( ! workspace ) { return ; } const extension = this . getByName ( workspace . name ) ; if ( extension ) { command . callback . call ( thisArg , extension , editor , ... args ) ; } } ) ; } } } onDidChangeConfiguration ( e ) { if ( e . affectsConfiguration ( 'jest' ) ) { this . applySettings ( getExtensionWindowSettings ( ) ) ; } vscode . workspace . workspaceFolders ?. forEach ( ( workspaceFolder ) => { const jestExt = this . getByName ( workspaceFolder . name ) ; if ( jestExt && e . affectsConfiguration ( 'jest' , workspaceFolder . uri ) ) { jestExt . triggerUpdateSettings ( ) ; } } ) ; } onDidChangeWorkspaceFolders ( e ) { e . added . forEach ( this . register , this ) ; e . removed . forEach ( this . unregister , this ) ; } onDidCloseTextDocument ( document ) { const ext = this . getByDocUri ( document . uri ) ; if ( ext ) { ext . onDidCloseTextDocument ( document ) ; } } onDidChangeActiveTextEditor ( editor ? ) { if ( editor && editor . document ) { statusBar . onDidChangeActiveTextEditor ( editor ) ; const ext = this . getByDocUri ( editor . document . uri ) ; if ( ext ) { ext . onDidChangeActiveTextEditor ( editor ) ; } } } onDidChangeTextDocument ( event ) { const ext = this . getByDocUri ( event . document . uri ) ; if ( ext ) { ext . onDidChangeTextDocument ( event ) ; } } onWillSaveTextDocument ( event ) { const ext = this . getByDocUri ( event . document . uri ) ; if ( ext ) { ext . onWillSaveTextDocument ( event ) ; } } onDidSaveTextDocument ( document ) { const ext = this . getByDocUri ( document . uri ) ; if ( ext ) { ext . onDidSaveTextDocument ( document ) ; } } private onFilesChange ( files , handler ) { const exts = files . map ( ( f ) => this . getByDocUri ( f ) ) . filter ( ( ext ) => ext != null ) as JestExt [ ] ; const set = new Set < JestExt > ( exts ) ; set . forEach ( handler ) ; } onDidCreateFiles ( event ) { this . onFilesChange ( event . files , ( ext ) => ext . onDidCreateFiles ( event ) ) ; } onDidDeleteFiles ( event ) { this . onFilesChange ( event . files , ( ext ) => ext . onDidDeleteFiles ( event ) ) ; } onDidRenameFiles ( event ) { const files = event . files . reduce ( ( list , f ) => { list . push ( f . newUri , f . oldUri ) ; return list ; } , [ ] as vscode . Uri [ ] ) ; this . onFilesChange ( files , ( ext ) => ext . onDidRenameFiles ( event ) ) ; } activate ( ) { if ( vscode . window . activeTextEditor ?. document . uri ) { const ext = this . getByDocUri ( vscode . window . activeTextEditor . document . uri ) ; if ( ext ) { ext . activate ( ) ; } } } }