/**
 * This file is part of the vscode-helpers distribution.
 * Copyright (c) Marcel Joachim Kloubert.
 *
 * vscode-helpers is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as
 * published by the Free Software Foundation, version 3.
 *
 * vscode-helpers is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program. If not, see <http://www.gnu.org/licenses/>.
 */ import * as _ from 'lodash' ; import * as ChildProcess from 'child_process' ; import * as Crypto from 'crypto' ; import * as Enumerable from 'node-enumerable' ; import * as FS from 'fs' ; const IsBinaryFile = require ( "isbinaryfile" ) ; import * as IsStream from 'is-stream' ; const MergeDeep = require ( 'merge-deep' ) ; import * as Minimatch from 'minimatch' ; import * as Moment from 'moment' ; import * as OS from 'os' ; import * as Path from 'path' ; import { default as PQueue , QueueAddOptions } from 'p-queue' ; import * as Stream from 'stream' ; import * as vscode from 'vscode' ; import * as vscode_helpers_devtools from './devtools' ; import * as vscode_helpers_events from './events' ; import * as vscode_helpers_scm_git from './scm/git' ; import * as MomentTimeZone from 'moment-timezone' ; export * from './cache' ; export * from './devtools' ; export * from './disposable' ; export * from './events' ; export * from './fs' ; export * from './html' ; export * from './http' ; export * from './logging' ; export { from , range , repeat } from 'node-enumerable' ; export * from './notifications' ; export * from './progress' ; export * from './timers' ; export * from './workflows' ; export * from './workspaces' ; /**
 * Result of a file execution.
 */ export interface ExecFileResult { /**
     * The output from 'standard error' stream.
     */ stdErr : Buffer ; /**
     * The output from 'standard output' stream.
     */ stdOut : Buffer ; /**
     * The underlying process.
     */ process : ChildProcess . ChildProcess ; } param { T } item param { number } index param { T [ ] } array return { TResult | PromiseLike < TResult > } export type ForEachAsyncAction < T , TResult > = ( item : T , index : number , array : T [ ] ) => TResult | PromiseLike < TResult > ; /**
 * Describes the structure of the package file (package.json).
 */ export interface PackageFile { /**
     * The display name.
     */ displayName ? : string ; /**
     * The (internal) name.
     */ name ? : string ; /**
     * The version string.
     */ version ? : string ; } /**
 * Options for 'openAndShowTextDocument()' function.
 */ export type OpenAndShowTextDocumentOptions = string | { /**
     * The initial content.
     */ content ? : string ; /**
     * The language.
     */ language ? : string ; } ; param { any } err param { TResult } result export type SimpleCompletedAction < TResult > = ( err : any , result ? : TResult ) => void ; param { TStr } str return { string } export type StringNormalizer < TStr = string > = ( str : TStr ) => string ; const readFile = FS . promises . readFile ; let extensionRoot ; /**
 * Is AIX or not.
 */ export const IS_AIX = process . platform === 'aix' ; /**
 * Is Free BSD or not.
 */ export const IS_FREE_BSD = process . platform === 'freebsd' ; /**
 * Is Linux or not.
 */ export const IS_LINUX = process . platform === 'linux' ; /**
 * Is Sun OS or not.
 */ export const IS_MAC = process . platform === 'darwin' ; /**
 * Is Open BSD or not.
 */ export const IS_OPEN_BSD = process . platform === 'openbsd' ; /**
 * Is Sun OS or not.
 */ export const IS_SUNOS = process . platform === 'sunos' ; /**
 * Is Windows or not.
 */ export const IS_WINDOWS = process . platform === 'win32' ; /**
 * Global execution queue, which only allows one execution at the same time.
 */ export const QUEUE = new PQueue ( { autoStart : true , concurrency : 1 , } ) ; /**
 * Stores global data for the current extension session.
 */ export const SESSION = { } ; /**
 * Disposes 'SESSION', by removing its data.
 */ export const SESSION_DISPOSER = { inheritdoc dispose : ( ) => { for ( const P of Object . keys ( SESSION ) ) { delete SESSION [ P ] ; } } } ; param { TFunc } func param { any } thisArgs return { TFunc } export function applyFuncFor < TFunc extends Function = Function > ( func , thisArgs ) { return < any > function ( ) { return func . apply ( thisArgs , arguments ) ; } ; } param { T | T [ ] | ReadonlyArray < T > } val param { boolean } removeEmpty return { T [ ] } export function asArray < T > ( val , removeEmpty = true ) { removeEmpty = toBooleanSafe ( removeEmpty , true ) ; return ( _ . isArrayLike ( val ) ? val : [ val ] ) . filter ( i => { if ( removeEmpty ) { return ! _ . isNil ( i ) ; } return true ; } ) ; } param { any } val param { string } enc param { number } maxDepth return { Promise < Buffer > } export async function asBuffer ( val , enc ? , maxDepth ? ) { return await asBufferInner ( val , enc , null , maxDepth ) ; } async function asBufferInner ( val , enc ? , funcDepth ? , maxDepth ? ) { enc = normalizeString ( enc ) ; if ( '' === enc ) { enc = undefined ; } if ( isNaN ( funcDepth ) ) { funcDepth = 0 ; } if ( isNaN ( maxDepth ) ) { maxDepth = 63 ; } if ( funcDepth > maxDepth ) { throw new Error ( `Maximum depth of ${ maxDepth } reached!` ) ; } if ( Buffer . isBuffer ( val ) || _ . isNil ( val ) ) { return val ; } if ( _ . isFunction ( val ) ) { return asBufferInner ( await Promise . resolve ( val ( enc , funcDepth , maxDepth ) ) , enc , funcDepth + 1 , maxDepth ) ; } if ( IsStream . readable ( val ) ) { return await readAll ( val ) ; } if ( _ . isObject ( val ) ) { return new Buffer ( JSON . stringify ( val ) , enc ) ; } return new Buffer ( toStringSafe ( val ) , enc ) ; } param { any } val return { Moment . Moment } export function asLocalTime ( val ) { let localTime ; if ( ! _ . isNil ( val ) ) { if ( Moment . isMoment ( val ) ) { localTime = val ; } else if ( Moment . isDate ( val ) ) { localTime = Moment ( val ) ; } else { localTime = Moment ( toStringSafe ( val ) ) ; } } if ( localTime ) { if ( ! localTime . isLocal ( ) ) { localTime = localTime . local ( ) ; } } return localTime ; } param { any } val return { Moment . Moment } export function asUTC ( val ) { let utcTime ; if ( ! _ . isNil ( val ) ) { if ( Moment . isMoment ( val ) ) { utcTime = val ; } else if ( Moment . isDate ( val ) ) { utcTime = Moment ( val ) ; } else { utcTime = Moment ( toStringSafe ( val ) ) ; } } if ( utcTime ) { if ( ! utcTime . isUTC ( ) ) { utcTime = utcTime . utc ( ) ; } } return utcTime ; } param { T } val return { T } export function cloneObject < T > ( val ) { if ( ! val ) { return val ; } return JSON . parse ( JSON . stringify ( val ) ) ; } param { T } val param { boolean } useNewObjectForFunctions return { T } export function cloneObjectFlat < T > ( val , useNewObjectForFunctions = true ) { useNewObjectForFunctions = toBooleanSafe ( useNewObjectForFunctions , true ) ; if ( _ . isNil ( val ) ) { return val ; } const CLONED_OBJ = < any > { } ; const THIS_ARG = useNewObjectForFunctions ? CLONED_OBJ : val ; const ADD_PROPERTY = ( prop , value ) => { Object . defineProperty ( CLONED_OBJ , prop , { configurable : true , enumerable : true , get : ( ) => { return value ; } , set : ( newValue ) => { value = newValue ; } , } ) ; } ; _ . forIn ( val , ( value , prop ) => { let valueToSet = value ; if ( _ . isFunction ( valueToSet ) ) { const FUNC = valueToSet ; valueToSet = function ( ) { return FUNC . apply ( THIS_ARG , arguments ) ; } ; } ADD_PROPERTY ( prop , valueToSet ) ; } ) ; return CLONED_OBJ ; } param { T } x param { T } y return { number } export function compareValues < T > ( x , y ) { if ( x !== y ) { if ( x > y ) { return 1 ; } else if ( x < y ) { return - 1 ; } } return 0 ; } param { T } x param { T } y param { Function } selector return { number } export function compareValuesBy < T , U > ( x , y , selector ) { return compareValues ( selector ( x ) , selector ( y ) ) ; } /**
 * Alias for 'createDevToolsClient'.
 */ export function createChromeClient ( opts ? ) { return createDevToolsClient . apply ( null , arguments ) ; } param { Function } resolve param { Function } reject return { SimpleCompletedAction < TResult > } export function createCompletedAction < TResult = any > ( resolve , reject ? ) { let completedInvoked = false ; return ( err , result ? ) => { if ( completedInvoked ) { return ; } completedInvoked = true ; if ( err ) { if ( reject ) { reject ( err ) ; } } else { if ( resolve ) { resolve ( result ) ; } } } ; } param { vscode_helpers_devtools . DevToolsClientOptions } opts return { vscode_helpers_devtools . DevToolsClient } export function createDevToolsClient ( opts ? ) { return new vscode_helpers_devtools . DevToolsClient ( opts ) ; } param { string } cwd param { string } path return { Promise < vscode_helpers_scm_git . GitClient | false > } export function createGitClient ( cwd ? , path ? ) { return Promise . resolve ( createGitClientSync ( cwd , path ) ) ; } param { string } cwd param { string } path return { vscode_helpers_scm_git . GitClient | false } export function createGitClientSync ( cwd ? , path ? ) { const CLIENT = tryCreateGitClientSync ( cwd , path ) ; if ( false === CLIENT ) { throw new Error ( 'No git client found!' ) ; } return CLIENT ; } param { TOpts } opts return { PQueue } export function createQueue < TOpts extends QueueAddOptions = QueueAddOptions > ( opts ? ) { const DEFAULT_OPTS = { autoStart : true , concurrency : 1 , } ; return new PQueue ( MergeDeep ( DEFAULT_OPTS , opts ) ) ; } param { any } val param { string | string [ ] } patterns param { Minimatch . IOptions } options return { boolean } export function doesMatch ( val , patterns , options ? ) { val = toStringSafe ( val ) ; patterns = asArray ( patterns ) . map ( p => { return toStringSafe ( p ) ; } ) ; for ( const P of patterns ) { if ( Minimatch ( val , P , options ) ) { return true ; } } return false ; } param { string } command param { any [ ] } args param { ChildProcess . ExecFileOptions } opts return { Promise < ExecFileResult > } export async function execFile ( command , args ? , opts ? ) { command = toStringSafe ( command ) ; args = asArray ( args , false ) . map ( a => { return toStringSafe ( a ) ; } ) ; if ( ! opts ) { opts = { } ; } if ( _ . isNil ( opts . env ) ) { opts . env = process . env ; } return new Promise < ExecFileResult > ( ( resolve , reject ) => { const RESULT = { stdErr : undefined , stdOut : undefined , process : undefined , } ; let completedInvoked = false ; const COMPLETED = ( err ) => { if ( completedInvoked ) { return ; } completedInvoked = true ; if ( err ) { reject ( err ) ; } else { resolve ( RESULT ) ; } } ; try { const P = ChildProcess . execFile ( command , args , opts , ( err , stdout , stderr ) => { if ( err ) { COMPLETED ( err ) ; } else { try { RESULT . process = P ; ( async ( ) => { RESULT . stdErr = await asBuffer ( stderr , 'utf8' ) ; RESULT . stdOut = await asBuffer ( stdout , 'utf8' ) ; } ) ( ) . then ( ( ) => { COMPLETED ( null ) ; } , ( err ) => { COMPLETED ( err ) ; } ) ; } catch ( e ) { COMPLETED ( e ) ; } } } ) ; } catch ( e ) { COMPLETED ( e ) ; } } ) ; } param { Enumerable . Sequence < T > } items param { Function } action param { any } thisArg return { TResult } export async function forEachAsync < T , TResult > ( items , action , thisArg ? ) { if ( ! _ . isArrayLike ( items ) ) { items = Enumerable . from ( items ) . toArray ( ) ; } let lastResult ; for ( let i = 0 ; i < ( < ArrayLike < T > > items ) . length ; i ++ ) { lastResult = await Promise . resolve ( action . apply ( thisArg , [ items [ i ] , i , items ] ) ) ; } return lastResult ; } param { any } formatStr param { any [ ] } args return { string } export function format ( formatStr , ... args ) { return formatArray ( formatStr , args ) ; } param { any } formatStr param { Enumerable . Sequence < any > } args return { string } export function formatArray ( formatStr , args ) { formatStr = toStringSafe ( formatStr ) ; if ( ! _ . isArrayLike ( args ) ) { args = Enumerable . from ( args ) . toArray ( ) ; } return formatStr . replace ( /{(\d+)(\:)?([^}]*)}/g , ( match , index , formatSeparator , formatExpr ) => { index = parseInt ( toStringSafe ( index ) ) ; let resultValue = ( < ArrayLike < any > > args ) [ index ] ; if ( ':' === formatSeparator ) { const FORMAT_PROVIDERS = toStringSafe ( formatExpr ) . split ( ',' ) . map ( x => x . toLowerCase ( ) . trim ( ) ) . filter ( x => x ) ; FORMAT_PROVIDERS . forEach ( fp => { switch ( fp ) { case 'ending_space' : resultValue = toStringSafe ( resultValue ) ; if ( '' !== resultValue ) { resultValue = resultValue + ' ' ; } break ; case 'leading_space' : resultValue = toStringSafe ( resultValue ) ; if ( '' !== resultValue ) { resultValue = ' ' + resultValue ; } break ; case 'lower' : resultValue = toStringSafe ( resultValue ) . toLowerCase ( ) ; break ; case 'trim' : resultValue = toStringSafe ( resultValue ) . trim ( ) ; break ; case 'upper' : resultValue = toStringSafe ( resultValue ) . toUpperCase ( ) ; break ; case 'surround' : resultValue = toStringSafe ( resultValue ) ; if ( '' !== resultValue ) { resultValue = "'" + toStringSafe ( resultValue ) + "'" ; } break ; } } ) ; } if ( _ . isUndefined ( resultValue ) ) { return match ; } return toStringSafe ( resultValue ) ; } ) ; } return { string } export function getExtensionRoot ( ) { return extensionRoot ; } param { string } packageJson return { Promise < PackageFile > } export async function getPackageFile ( packageJson = '../package.json' ) { return JSON . parse ( ( await readFile ( getPackageFilePath ( packageJson ) ) ) . toString ( 'utf8' ) ) ; } function getPackageFilePath ( packageJson ? ) { packageJson = toStringSafe ( packageJson ) ; if ( '' === packageJson . trim ( ) ) { packageJson = '../package.json' ; } if ( ! Path . isAbsolute ( packageJson ) ) { packageJson = Path . join ( getExtensionRoot ( ) , packageJson ) ; } return Path . resolve ( packageJson ) ; } param { string } packageJson return { PackageFile } export function getPackageFileSync ( packageJson = '../package.json' ) { return JSON . parse ( ( FS . readFileSync ( getPackageFilePath ( packageJson ) ) ) . toString ( 'utf8' ) ) ; } /**
 * Alias for 'uuid'.
 */ export function guid ( ver ? , ... args ) { return uuid . apply ( this , arguments ) ; } param { Buffer } data returns { Promise < boolean > } export function isBinaryContent ( data ) { return new Promise < boolean > ( ( resolve , reject ) => { const COMPLETED = createCompletedAction < boolean > ( resolve , reject ) ; try { IsBinaryFile ( data , data . length , ( err , result ) => { COMPLETED ( err , result ) ; } ) ; } catch ( e ) { COMPLETED ( e ) ; } } ) ; } param { Buffer } data returns { boolean } export function isBinaryContentSync ( data ) { return IsBinaryFile . sync ( data , data . length ) ; } param { any } val return { boolean } export function isEmptyString ( val ) { return '' === toStringSafe ( val ) . trim ( ) ; } param { string } file param { boolean } fromCache return { TModule } export function loadModule < TModule = any > ( file , fromCache = false ) { file = toStringSafe ( file ) ; if ( isEmptyString ( file ) ) { file = './module.js' ; } if ( ! Path . isAbsolute ( file ) ) { file = Path . join ( process . cwd ( ) , file ) ; } file = Path . resolve ( file ) ; fromCache = toBooleanSafe ( fromCache ) ; if ( ! fromCache ) { delete require . cache [ file ] ; } return require ( file ) ; } param { any } val param { StringNormalizer } normalizer return { string } export function normalizeString ( val , normalizer ? ) { if ( ! normalizer ) { normalizer = ( str ) => str . toLowerCase ( ) . trim ( ) ; } return normalizer ( toStringSafe ( val ) ) ; } param { string } timezone return { Moment . Moment } export function now ( timezone ? ) { timezone = toStringSafe ( timezone ) . trim ( ) ; const NOW = Moment ( ) ; return '' === timezone ? NOW : NOW . tz ( timezone ) ; } param { OpenAndShowTextDocumentOptions } filenameOrOpts return { vscode . TextEditor } export async function openAndShowTextDocument ( filenameOrOpts ? ) { if ( _ . isNil ( filenameOrOpts ) ) { filenameOrOpts = { content : '' , language : 'plaintext' , } ; } return await vscode . window . showTextDocument ( await vscode . workspace . openTextDocument ( < any > filenameOrOpts ) ) ; } param { number } size return { Promise < Buffer > } export function randomBytes ( size ) { size = parseInt ( toStringSafe ( size ) . trim ( ) ) ; return new Promise < Buffer > ( ( resolve , reject ) => { const COMPLETED = createCompletedAction ( resolve , reject ) ; Crypto . randomBytes ( size , ( err , buf ) => { COMPLETED ( err , buf ) ; } ) ; } ) ; } param { Stream . Readable } stream param { string } enc returns { Promise < Buffer > } export function readAll ( stream , enc ? ) { enc = normalizeString ( enc ) ; if ( '' === enc ) { enc = undefined ; } return new Promise < Buffer > ( ( resolve , reject ) => { let buff ; let dataListener ; let endListener ; let errorListener ; let completedInvoked = false ; const COMPLETED = ( err ) => { if ( completedInvoked ) { return ; } completedInvoked = true ; vscode_helpers_events . tryRemoveListener ( stream , 'data' , dataListener ) ; vscode_helpers_events . tryRemoveListener ( stream , 'end' , endListener ) ; vscode_helpers_events . tryRemoveListener ( stream , 'error' , errorListener ) ; if ( err ) { reject ( err ) ; } else { resolve ( buff ) ; } } ; if ( _ . isNil ( stream ) ) { buff = < any > stream ; COMPLETED ( null ) ; return ; } errorListener = ( err ) => { if ( err ) { COMPLETED ( err ) ; } } ; dataListener = ( chunk ) => { try { if ( ! chunk || chunk . length < 1 ) { return ; } if ( _ . isString ( chunk ) ) { chunk = new Buffer ( chunk , enc ) ; } buff = Buffer . concat ( [ buff , chunk ] ) ; } catch ( e ) { COMPLETED ( e ) ; } } ; endListener = ( ) => { COMPLETED ( null ) ; } ; try { stream . on ( 'error' , errorListener ) ; buff = Buffer . alloc ( 0 ) ; stream . once ( 'end' , endListener ) ; stream . on ( 'data' , dataListener ) ; } catch ( e ) { COMPLETED ( e ) ; } } ) ; } param { string } path return { string } export function setExtensionRoot ( path ) { path = toStringSafe ( path ) ; if ( '' === path . trim ( ) ) { path = undefined ; } else { if ( ! Path . isAbsolute ( path ) ) { path = Path . join ( process . cwd ( ) , path ) ; } path = Path . resolve ( path ) ; } extensionRoot = path ; return path ; } param { Enumerable . Sequence < T > } seq param { boolean } normalize return { T [ ] } export function toArray < T > ( seq , normalize = true ) { if ( _ . isNil ( seq ) ) { if ( toBooleanSafe ( normalize , true ) ) { return [ ] ; } return < any > seq ; } if ( _ . isArrayLike ( seq ) ) { const NEW_ARRAY = [ ] ; for ( let i = 0 ; i < seq . length ; i ++ ) { NEW_ARRAY . push ( seq [ i ] ) ; } return NEW_ARRAY ; } return Enumerable . from ( seq ) . toArray ( ) ; } param { any } val param { boolean } defaultVal return { boolean } export function toBooleanSafe ( val , defaultVal = false ) { if ( _ . isBoolean ( val ) ) { return val ; } if ( _ . isNil ( val ) ) { return ! ! defaultVal ; } return ! ! val ; } param { vscode . EndOfLine } eol return export function toEOL ( eol ? ) { switch ( eol ) { case vscode . EndOfLine . CRLF : return "\r\n" ; case vscode . EndOfLine . LF : return "\n" ; } return OS . EOL ; } param { any } val param { string } defaultVal return { string } export function toStringSafe ( val , defaultVal = '' ) { if ( _ . isString ( val ) ) { return val ; } if ( _ . isNil ( val ) ) { return '' + defaultVal ; } try { if ( val instanceof Error ) { return '' + val . message ; } if ( _ . isFunction ( val [ 'toString' ] ) ) { return '' + val . toString ( ) ; } if ( _ . isObject ( val ) ) { return JSON . stringify ( val ) ; } } catch { } return '' + val ; } param { string } cwd param { string } path return { Promise < vscode_helpers_scm_git . GitClient | false > } export function tryCreateGitClient ( cwd ? , path ? ) { return Promise . resolve ( < any > tryCreateGitClientSync ( cwd , path ) ) ; } param { string } cwd param { string } path return { vscode_helpers_scm_git . GitClient | false } export function tryCreateGitClientSync ( cwd ? , path ? ) { const GIT_EXEC = vscode_helpers_scm_git . tryFindGitPathSync ( path ) ; if ( false !== GIT_EXEC ) { return new vscode_helpers_scm_git . GitClient ( GIT_EXEC , cwd ) ; } return false ; } return { Moment . Moment } export function utcNow ( ) { return Moment . utc ( ) ; } param { string } ver param { any [ ] } args return { string } export function uuid ( ver ? , ... args ) { const UUID = require ( 'uuid' ) ; ver = normalizeString ( ver ) ; let func = false ; switch ( ver ) { case '' : case '4' : case 'v4' : func = UUID . v4 ; break ; case '1' : case 'v1' : func = UUID . v1 ; break ; case '5' : case 'v5' : func = UUID . v5 ; break ; } if ( false === func ) { throw new Error ( `Version '${ ver }' is not supported!` ) ; } return func . apply ( null , args ) ; }