import { Disposable , ExtensionContext , Uri , tasks , TaskProvider , workspace , window , FileSystemWatcher , ConfigurationChangeEvent , WorkspaceFolder , Task } from "vscode" ; import { TaskTreeDataProvider } from "./tree/tree" ; import { AntTaskProvider } from "./providers/ant" ; import { MakeTaskProvider } from "./providers/make" ; import { MavenTaskProvider } from "./providers/maven" ; import { ScriptTaskProvider } from "./providers/script" ; import { GradleTaskProvider } from "./providers/gradle" ; import { GruntTaskProvider } from "./providers/grunt" ; import { PipenvTaskProvider } from "./providers/pipenv" ; import { GulpTaskProvider } from "./providers/gulp" ; import { AppPublisherTaskProvider } from "./providers/appPublisher" ; import { configuration } from "./common/configuration" ; import { initStorage } from "./common/storage" ; import { views } from "./views" ; import { TaskExplorerProvider } from "./providers/provider" ; import * as util from "./common/utils" ; import * as cache from "./cache" ; import * as log from "./common/log" ; export let treeDataProvider ; export let treeDataProvider2 ; export let appDataPath ; const watchers = new Map ( ) ; const watcherDisposables = new Map ( ) ; export const providers = new Map ( ) ; export interface TaskExplorerApi { explorerProvider : TaskTreeDataProvider | undefined ; sidebarProvider : TaskTreeDataProvider | undefined ; utilities : any ; fileCache : any ; } export async function activate ( context , disposables ) { log . initLog ( "taskExplorer" , "Task Explorer" , context ) ; initStorage ( context ) ; log . write ( "" ) ; log . write ( "Init extension" ) ; await registerFileWatchers ( context ) ; registerTaskProviders ( context ) ; if ( configuration . get < boolean > ( "enableSideBar" ) ) { treeDataProvider = registerExplorer ( "taskExplorerSideBar" , context ) ; } if ( configuration . get < boolean > ( "enableExplorerView" ) ) { treeDataProvider2 = registerExplorer ( "taskExplorer" , context ) ; } const workspaceWatcher = workspace . onDidChangeWorkspaceFolders ( async ( _e ) => { await addWsFolder ( _e . added ) ; await removeWsFolder ( _e . removed ) ; await refreshTree ( ) ; } ) ; context . subscriptions . push ( workspaceWatcher ) ; const d = workspace . onDidChangeConfiguration ( async ( e ) => { await processConfigChanges ( context , e ) ; } ) ; context . subscriptions . push ( d ) ; log . write ( "   Task Explorer activated" ) ; return { explorerProvider : treeDataProvider2 , sidebarProvider : treeDataProvider , utilities : util , fileCache : cache } ; } export async function addWsFolder ( wsf ) { if ( wsf ) { for ( const f in wsf ) { if ( wsf . hasOwnProperty ( f ) ) { log . write ( "Workspace folder added: " + wsf [ f ] . name , 1 ) ; await cache . addFolderToCache ( wsf [ f ] ) ; } } } } export async function deactivate ( ) { for ( const [ k , d ] of watcherDisposables ) { d . dispose ( ) ; } for ( const [ k , w ] of watchers ) { w . dispose ( ) ; } await cache . cancelBuildCache ( true ) ; } export async function removeWsFolder ( wsf , logPad = "" ) { log . methodStart ( "process remove workspace folder" , 1 , logPad , true ) ; if ( ! wsf ) { return ; } for ( const f of wsf ) { log . value ( "      folder" , f . name , 1 , logPad ) ; await util . forEachMapAsync ( cache . filesCache , ( files , provider ) => { const toRemove = [ ] ; log . value ( "      start remove task files from cache" , provider , 2 , logPad ) ; for ( const file of files ) { log . value ( "         checking cache file" , file . uri . fsPath , 4 , logPad ) ; if ( file . folder . uri . fsPath === f . uri . fsPath ) { log . write ( "            added for removal" , 4 , logPad ) ; toRemove . push ( file ) ; } } if ( toRemove . length > 0 ) { for ( const tr of toRemove ) { log . value ( "         remove file" , tr . uri . fsPath , 2 , logPad ) ; files . delete ( tr ) ; } } log . value ( "      completed remove files from cache" , provider , 2 , logPad ) ; } ) ; log . write ( "   folder removed" , 1 , logPad ) ; } log . methodDone ( "process remove workspace folder" , 1 , logPad , true ) ; } async function processConfigChanges ( context , e ) { let refresh = false ; const refreshTaskTypes = [ ] , taskTypes = util . getTaskTypes ( ) ; const registerChange = ( taskType ) => { if ( util . existsInArray ( refreshTaskTypes , taskType ) === false ) { refreshTaskTypes . push ( taskType ) ; } } ; if ( configuration . get < boolean > ( "autoRefresh" ) === false ) { return ; } if ( e . affectsConfiguration ( "taskExplorer.exclude" ) ) { refresh = true ; } if ( e . affectsConfiguration ( "taskExplorer.groupWithSeparator" ) || e . affectsConfiguration ( "taskExplorer.groupSeparator" ) || e . affectsConfiguration ( "taskExplorer.groupMaxLevel" ) || e . affectsConfiguration ( "taskExplorer.groupStripTaskLabel" ) ) { refresh = true ; } if ( e . affectsConfiguration ( "taskExplorer.showLastTasks" ) ) { if ( configuration . get < boolean > ( "enableSideBar" ) && treeDataProvider ) { await treeDataProvider . showSpecialTasks ( configuration . get < boolean > ( "showLastTasks" ) ) ; } if ( configuration . get < boolean > ( "enableExplorerView" ) && treeDataProvider2 ) { await treeDataProvider2 . showSpecialTasks ( configuration . get < boolean > ( "showLastTasks" ) ) ; } } for ( const i in taskTypes ) { if ( taskTypes . hasOwnProperty ( i ) ) { const taskType = taskTypes [ i ] , taskTypeP = taskType !== "app-publisher" ? util . properCase ( taskType ) : "AppPublisher" , enabledSetting = "enable" + taskTypeP ; if ( e . affectsConfiguration ( "taskExplorer." + enabledSetting ) ) { const ignoreModify = util . isScriptType ( taskType ) || taskType === "app-publisher" ; await registerFileWatcher ( context , taskType , util . getGlobPattern ( taskType ) , ignoreModify , configuration . get < boolean > ( enabledSetting ) ) ; registerChange ( taskType ) ; } } } for ( const type of util . getTaskTypes ( ) ) { if ( type === "app-publisher" ) { if ( e . affectsConfiguration ( "taskExplorer.pathToAppPublisher" ) ) { refreshTaskTypes . push ( "app-publisher" ) ; } } else if ( e . affectsConfiguration ( "taskExplorer.pathTo" + util . properCase ( type ) ) ) { refreshTaskTypes . push ( type ) ; } } if ( e . affectsConfiguration ( "taskExplorer.includeAnt" ) ) { if ( util . existsInArray ( refreshTaskTypes , "ant" ) === false ) { await registerFileWatcher ( context , "ant" , util . getAntGlobPattern ( ) , false , configuration . get < boolean > ( "enableAnt" ) ) ; registerChange ( "ant" ) ; } } if ( e . affectsConfiguration ( "taskExplorer.useAnt" ) ) { registerChange ( "ant" ) ; } if ( e . affectsConfiguration ( "taskExplorer.useGulp" ) ) { registerChange ( "gulp" ) ; } if ( e . affectsConfiguration ( "npm.packageManager" , undefined ) ) { registerChange ( "npm" ) ; } if ( e . affectsConfiguration ( "taskExplorer.enableSideBar" ) ) { if ( configuration . get < boolean > ( "enableSideBar" ) ) { if ( treeDataProvider ) { refresh = true ; } else { treeDataProvider = registerExplorer ( "taskExplorerSideBar" , context ) ; } } } if ( e . affectsConfiguration ( "taskExplorer.enableExplorerView" ) ) { if ( configuration . get < boolean > ( "enableExplorerView" ) ) { if ( treeDataProvider2 ) { refresh = true ; } else { treeDataProvider2 = registerExplorer ( "taskExplorer" , context ) ; } } } if ( e . affectsConfiguration ( "terminal.integrated.shell.windows" ) || e . affectsConfiguration ( "terminal.integrated.shell.linux" ) || e . affectsConfiguration ( "terminal.integrated.shell.macos" ) ) { if ( configuration . get < boolean > ( "enableBash" ) || configuration . get < boolean > ( "enableBatch" ) || configuration . get < boolean > ( "enablePerl" ) || configuration . get < boolean > ( "enablePowershell" ) || configuration . get < boolean > ( "enablePython" ) || configuration . get < boolean > ( "enableRuby" ) || configuration . get < boolean > ( "enableNsis" ) ) { refresh = true ; } } if ( refresh ) { await refreshTree ( ) ; } else if ( refreshTaskTypes ?. length > 0 ) { for ( const t of refreshTaskTypes ) { await refreshTree ( t ) ; } } } async function registerFileWatchers ( context ) { const taskTypes = util . getTaskTypes ( ) ; for ( const t of taskTypes ) { const taskType = t , taskTypeP = taskType !== "app-publisher" ? util . properCase ( taskType ) : "AppPublisher" ; if ( configuration . get < boolean > ( "enable" + taskTypeP ) ) { const watchModify = util . isScriptType ( taskType ) || taskType === "app-publisher" ; await registerFileWatcher ( context , taskType , util . getGlobPattern ( taskType ) , watchModify ) ; } } } export async function refreshTree ( taskType ? , uri ? ) { if ( uri && util . isExcluded ( uri . path ) ) { return ; } if ( configuration . get < boolean > ( "enableSideBar" ) && treeDataProvider ) { await treeDataProvider . refresh ( taskType , uri ) ; } if ( configuration . get < boolean > ( "enableExplorerView" ) && treeDataProvider2 ) { await treeDataProvider2 . refresh ( taskType , uri ) ; } } function registerTaskProvider ( providerName , provider , context ) { context . subscriptions . push ( tasks . registerTaskProvider ( providerName , provider ) ) ; providers . set ( providerName , provider ) ; } function registerTaskProviders ( context ) { registerTaskProvider ( "ant" , new AntTaskProvider ( ) , context ) ; registerTaskProvider ( "app-publisher" , new AppPublisherTaskProvider ( ) , context ) ; registerTaskProvider ( "gradle" , new GradleTaskProvider ( ) , context ) ; registerTaskProvider ( "grunt" , new GruntTaskProvider ( ) , context ) ; registerTaskProvider ( "gulp" , new GulpTaskProvider ( ) , context ) ; registerTaskProvider ( "make" , new MakeTaskProvider ( ) , context ) ; registerTaskProvider ( "maven" , new MavenTaskProvider ( ) , context ) ; registerTaskProvider ( "pipenv" , new PipenvTaskProvider ( ) , context ) ; registerTaskProvider ( "script" , new ScriptTaskProvider ( ) , context ) ; } async function registerFileWatcher ( context , taskType , fileBlob , ignoreModify ? , enabled ? ) { log . write ( "Register file watcher for task type '" + taskType + "'" ) ; let watcher = watchers . get ( taskType ) ; if ( workspace . workspaceFolders ) { await cache . buildCache ( taskType , fileBlob ) ; } if ( watcher ) { const watcherDisposable = watcherDisposables . get ( taskType ) ; if ( watcherDisposable ) { watcherDisposable . dispose ( ) ; watcherDisposables . delete ( taskType ) ; } } if ( enabled !== false ) { if ( ! watcher ) { watcher = workspace . createFileSystemWatcher ( fileBlob ) ; watchers . set ( taskType , watcher ) ; context . subscriptions . push ( watcher ) ; } if ( ! ignoreModify ) { watcherDisposables . set ( taskType , watcher . onDidChange ( async ( _e ) => { logFileWatcherEvent ( _e , "change" ) ; await refreshTree ( taskType , _e ) ; } ) ) ; } watcherDisposables . set ( taskType , watcher . onDidDelete ( async ( _e ) => { logFileWatcherEvent ( _e , "delete" ) ; await cache . removeFileFromCache ( taskType , _e ) ; await refreshTree ( taskType , _e ) ; } ) ) ; watcherDisposables . set ( taskType , watcher . onDidCreate ( async ( _e ) => { logFileWatcherEvent ( _e , "create" ) ; await cache . addFileToCache ( taskType , _e ) ; await refreshTree ( taskType , _e ) ; } ) ) ; } } function logFileWatcherEvent ( uri , type ) { log . write ( "file change event" ) ; log . value ( "   type" , type ) ; log . value ( "   file" , uri . fsPath ) ; } function registerExplorer ( name , context , enabled ? ) { log . write ( "Register explorer view / tree provider '" + name + "'" ) ; if ( enabled !== false ) { if ( workspace . workspaceFolders ) { const treeDataProvider = new TaskTreeDataProvider ( name , context ) ; const treeView = window . createTreeView ( name , { treeDataProvider , showCollapseAll : true } ) ; views . set ( name , treeView ) ; const view = views . get ( name ) ; if ( view ) { context . subscriptions . push ( view ) ; log . write ( "   Tree data provider registered'" + name + "'" ) ; } return treeDataProvider ; } else { log . write ( "✘ No workspace folders!!!" ) ; } } }