import * as cp from "child_process" ; import * as vscode from "vscode" ; import { logger , LogLevel , getBinPath , getConfiguration , errMsgRegex , } from "./util" ; import { langId } from "./consts" ; import * as path from "path" ; import * as fs from "fs" ; export enum CompileMode { onSave , onCompile , onCompileSpecific } type CompilerProcessType = { compileMode : CompileMode ; process : cp . ChildProcessWithoutNullStreams ; } ; let compileProcess = undefined ; let compileOutputChannel = undefined ; export const initCompile = ( ) => { compileOutputChannel = vscode . window . createOutputChannel ( `VSLilyPond: Compilation` ) ; } ; const getCompilingStatusBarItem = ( ) => { const item = vscode . window . createStatusBarItem ( vscode . StatusBarAlignment . Left , 0 ) ; item . text = `$(sync~spin) Compiling...` ; item . tooltip = `You can kill the compilation process using the \`VSLilyPond: Kill Compilation Process\` command` ; return item ; } ; const showCompilationFailedStatusBarItem = ( timeoutMS = 5000 ) => { const item = vscode . window . createStatusBarItem ( vscode . StatusBarAlignment . Left , 0 ) ; item . text = `$(x) Compilation Failed` ; item . tooltip = `See the \`VSLilyPond: Compilation\` output for more information` ; item . show ( ) ; setTimeout ( ( ) => item . hide ( ) , timeoutMS ) ; } ; const outputToChannel = async ( msg , show = false ) => { if ( compileOutputChannel ) { compileOutputChannel . appendLine ( msg ) ; if ( show ) { compileOutputChannel . show ( true ) ; } } else { logger ( `Unable to output to Compile OutputChannel, ${ msg }` , LogLevel . warning , true ) ; } } ; const getCompilationFilePath = ( compileMode , activeTextDocument ) => { const config = getConfiguration ( activeTextDocument ) ; if ( config . compilation . pathToMainCompilationFile . trim ( ) . length > 0 ) { if ( ( compileMode === CompileMode . onSave && config . compilation . compileMainFileOnSave ) || compileMode === CompileMode . onCompile ) { const workspaceFolderPath = vscode . workspace . getWorkspaceFolder ( activeTextDocument . uri ) ?. uri . fsPath ; if ( workspaceFolderPath ) { const compileFilePath = path . join ( workspaceFolderPath , config . compilation . pathToMainCompilationFile . trim ( ) ) ; if ( fs . existsSync ( compileFilePath ) ) { return compileFilePath ; } else { throw new Error ( `Unable to find main file to compile: file does not exist (${ compileFilePath })` ) ; } } else { throw new Error ( `Unable to find main file to compile: unable to get workspace folder path of the currently active text document` ) ; } } } return activeTextDocument . uri . fsPath ; } ; export const killCompilation = async ( mute = false ) => { if ( compileProcess ) { compileProcess . process . kill ( `SIGKILL` ) ; compileProcess = undefined ; logger ( `Compilation process killed` , LogLevel . info , mute ) ; } else { logger ( `No active compilation process running` , LogLevel . info , mute ) ; } } ; export const processStderr = async ( output ) => { let errGroup = null ; while ( ( errGroup = errMsgRegex . exec ( output ) ) ) { const channelOutput = `${ errGroup [ 4 ] . toLocaleUpperCase ( ) }: ${ errGroup [ 0 ] }` ; outputToChannel ( channelOutput , errGroup [ 4 ] === `error` ) ; } } ; export const compile = async ( compileMode = CompileMode . onCompile , mute = false , textDocument = undefined ) => { const compilingStatasBarItem = getCompilingStatusBarItem ( ) ; compilingStatasBarItem . show ( ) ; try { if ( compileProcess ) { killCompilation ( true ) ; } const binPath = getBinPath ( ) ; const activeTextDocument = textDocument ?? vscode . window . activeTextEditor ?. document ; if ( ! activeTextDocument ) { throw new Error ( `No active text editor open` ) ; } const docLangId = activeTextDocument . languageId ; if ( docLangId !== langId ) { throw new Error ( `Only Lilypond files are supported` ) ; } const filePath = getCompilationFilePath ( compileMode , activeTextDocument ) ; const config = getConfiguration ( activeTextDocument ) ; const additionalArgs = config . compilation . additionalCommandLineArguments . trim ( ) . split ( /\s+/ ) ; const args = [ `--loglevel=WARNING` ] . concat ( additionalArgs ) . concat ( filePath ) ; if ( compileMode === CompileMode . onSave ) { outputToChannel ( `[SAVED]: ${ textDocument ?. uri . fsPath }` ) ; } outputToChannel ( `Compiling: ${ filePath }` ) ; logger ( `Compiling...` , LogLevel . info , mute ) ; compileProcess = { compileMode : compileMode , process : cp . spawn ( binPath , args , { cwd : path . dirname ( filePath ) } ) , } ; compileProcess . process . stdout . on ( "data" , ( data ) => { outputToChannel ( `${ data . toString ( ) }` ) ; } ) ; compileProcess . process . stderr . on ( "data" , ( data ) => { outputToChannel ( `${ data . toString ( ) }` ) ; processStderr ( data . toString ( ) ) ; } ) ; compileProcess . process . on ( "close" , ( code ) => { logger ( `Compilation process exited with code ${ code }` , LogLevel . info , true ) ; if ( code === 0 ) { logger ( `Compilation successful` , LogLevel . info , mute ) ; outputToChannel ( `Compilation successful` ) ; } else if ( code === null ) { logger ( `Compilation killed` , LogLevel . error , mute ) ; outputToChannel ( `Compilation killed` , false ) ; } else { logger ( `Compilation failed` , LogLevel . error , mute ) ; outputToChannel ( `Compilation failed` ) ; showCompilationFailedStatusBarItem ( ) ; } compileProcess = undefined ; compilingStatasBarItem . hide ( ) ; } ) ; } catch ( err ) { logger ( String ( err ) , LogLevel . error , mute ) ; outputToChannel ( `Compilation failed: ${ err }` , true ) ; showCompilationFailedStatusBarItem ( ) ; compilingStatasBarItem . hide ( ) ; } } ;