import * as cp from "child_process" ; import * as fs from "fs" ; import * as open from "open" ; import * as path from "path" ; import * as vscode from "vscode" ; import { CONFIG , CONSTANTS , CPX_CONFIG_FILE , DialogResponses , GLOBAL_ENV_VARS , HELPER_FILES , LANGUAGE_VARS , SERVER_INFO , TelemetryEventName , } from "./constants" ; import { CPXWorkspace } from "./cpxWorkspace" ; import { DebugAdapterFactory } from "./debugger/debugAdapterFactory" ; import { DebuggerCommunicationServer } from "./debuggerCommunicationServer" ; import * as utils from "./extension_utils/utils" ; import { SerialMonitor } from "./serialMonitor" ; import { DebuggerCommunicationService } from "./service/debuggerCommunicationService" ; import { DeviceSelectionService } from "./service/deviceSelectionService" ; import { FileSelectionService } from "./service/fileSelectionService" ; import { MessagingService } from "./service/messagingService" ; import { PopupService } from "./service/PopupService" ; import { SetupService } from "./service/setupService" ; import { TelemetryHandlerService } from "./service/telemetryHandlerService" ; import { WebviewService } from "./service/webviewService" ; import { SimulatorDebugConfigurationProvider } from "./simulatorDebugConfigurationProvider" ; import getPackageInfo from "./telemetry/getPackageInfo" ; import TelemetryAI from "./telemetry/telemetryAI" ; import { UsbDetector } from "./usbDetector" ; import { VSCODE_MESSAGES_TO_WEBVIEW , WEBVIEW_MESSAGES , WEBVIEW_TYPES , } from "./view/constants" ; let telemetryAI ; let pythonExecutablePath = GLOBAL_ENV_VARS . PYTHON ; let configFileCreated = false ; let inDebugMode = false ; let firstTimeClosed = true ; let shouldShowRunCodePopup = true ; let setupService ; const deviceSelectionService = new DeviceSelectionService ( ) ; const messagingService = new MessagingService ( deviceSelectionService ) ; const debuggerCommunicationService = new DebuggerCommunicationService ( ) ; const fileSelectionService = new FileSelectionService ( messagingService ) ; let pythonProcessDataBuffer ; export let outChannel ; const sendCurrentDeviceMessage = ( currentPanel ) => { if ( currentPanel ) { currentPanel . webview . postMessage ( { command : VSCODE_MESSAGES_TO_WEBVIEW . SET_DEVICE , active_device : deviceSelectionService . getCurrentActiveDevice ( ) , } ) ; } } ; export async function activate ( context ) { telemetryAI = new TelemetryAI ( context ) ; setupService = new SetupService ( telemetryAI ) ; let currentPanel ; let childProcess ; let messageListener ; let activeEditorListener ; const webviewService = new WebviewService ( context , deviceSelectionService ) ; const telemetryHandlerService = new TelemetryHandlerService ( telemetryAI , deviceSelectionService ) ; const formalNameToNickNameMapping = { [ CONSTANTS . DEVICE_NAME_FORMAL . CPX ] : CONSTANTS . DEVICE_NAME . CPX , [ CONSTANTS . DEVICE_NAME_FORMAL . MICROBIT ] : CONSTANTS . DEVICE_NAME . MICROBIT , [ CONSTANTS . DEVICE_NAME_FORMAL . CLUE ] : CONSTANTS . DEVICE_NAME . CLUE , } ; updatePythonExtraPaths ( ) ; updatePylintArgs ( context ) ; pythonExecutablePath = await setupService . setupEnv ( context ) ; try { utils . generateCPXConfig ( ) ; configFileCreated = true ; } catch ( err ) { console . info ( "Failed to create the CPX config file." ) ; configFileCreated = false ; } if ( pythonExecutablePath === "" ) { return ; } if ( outChannel === undefined ) { outChannel = vscode . window . createOutputChannel ( CONSTANTS . NAME ) ; utils . logToOutputChannel ( outChannel , CONSTANTS . INFO . WELCOME_OUTPUT_TAB ) ; } vscode . workspace . onDidSaveTextDocument ( async ( document ) => { await fileSelectionService . updateCurrentFileFromTextFile ( document ) ; } ) ; const currVersionReleaseName = "release_note_" + getPackageInfo ( context ) . extensionVersion ; const viewedReleaseNote = context . globalState . get ( currVersionReleaseName , false ) ; if ( ! viewedReleaseNote ) { PopupService . openReleaseNote ( ) ; context . globalState . update ( currVersionReleaseName , true ) ; } const openWebview = ( ) => { if ( currentPanel && currentPanel . webview ) { messagingService . setWebview ( currentPanel . webview ) ; currentPanel . webview . html = webviewService . getWebviewContent ( WEBVIEW_TYPES . SIMULATOR , true , currentPanel ) ; currentPanel . reveal ( vscode . ViewColumn . Beside ) ; } else { currentPanel = vscode . window . createWebviewPanel ( CONSTANTS . WEBVIEW_TYPE . SIMULATOR , CONSTANTS . LABEL . WEBVIEW_PANEL , { preserveFocus : true , viewColumn : vscode . ViewColumn . Beside } , { localResourceRoots : [ vscode . Uri . file ( path . join ( context . extensionPath , CONSTANTS . FILESYSTEM . OUTPUT_DIRECTORY ) ) , ] , enableScripts : true , } ) ; currentPanel . webview . html = webviewService . getWebviewContent ( WEBVIEW_TYPES . SIMULATOR , true , currentPanel ) ; messagingService . setWebview ( currentPanel . webview ) ; if ( messageListener !== undefined ) { messageListener . dispose ( ) ; const index = context . subscriptions . indexOf ( messageListener ) ; if ( index > - 1 ) { context . subscriptions . splice ( index , 1 ) ; } } if ( activeEditorListener !== undefined ) { activeEditorListener . dispose ( ) ; const index = context . subscriptions . indexOf ( activeEditorListener ) ; if ( index > - 1 ) { context . subscriptions . splice ( index , 1 ) ; } } if ( currentPanel ) { messageListener = currentPanel . webview . onDidReceiveMessage ( message => { const messageJson = JSON . stringify ( { active_device : deviceSelectionService . getCurrentActiveDevice ( ) , state : message . text , } ) ; switch ( message . command ) { case WEBVIEW_MESSAGES . BUTTON_PRESS : telemetryHandlerService . handleButtonPressTelemetry ( message . text ) ; console . log ( `About to write ${ messageJson } \n` ) ; if ( inDebugMode && debuggerCommunicationService . getCurrentDebuggerServer ( ) ) { debuggerCommunicationService . getCurrentDebuggerServer ( ) . emitInputChanged ( messageJson ) ; } else if ( childProcess ) { childProcess . stdin . write ( messageJson + "\n" ) ; } break ; case WEBVIEW_MESSAGES . TOGGLE_PLAY_STOP : console . log ( `Play button ${ messageJson } \n` ) ; if ( message . text . state as boolean ) { fileSelectionService . setPathAndSendMessage ( message . text . selected_file ) ; fileSelectionService . findCurrentTextDocument ( ) ; telemetryAI . trackFeatureUsage ( TelemetryEventName . COMMAND_RUN_SIMULATOR_BUTTON ) ; runSimulatorCommand ( ) ; } else { killProcessIfRunning ( ) ; } if ( childProcess ) { childProcess . stdin . write ( messageJson + "\n" ) ; } break ; case WEBVIEW_MESSAGES . GESTURE : case WEBVIEW_MESSAGES . SENSOR_CHANGED : telemetryHandlerService . handleGestureTelemetry ( message . text ) ; console . log ( `Sensor changed ${ messageJson } \n` ) ; if ( inDebugMode && debuggerCommunicationService . getCurrentDebuggerServer ( ) ) { debuggerCommunicationService . getCurrentDebuggerServer ( ) . emitInputChanged ( messageJson ) ; } else if ( childProcess ) { childProcess . stdin . write ( messageJson + "\n" ) ; } break ; case WEBVIEW_MESSAGES . REFRESH_SIMULATOR : console . log ( "Refresh button" ) ; runSimulatorCommand ( ) ; break ; case WEBVIEW_MESSAGES . SLIDER_TELEMETRY : telemetryHandlerService . handleSensorTelemetry ( message . text ) ; break ; case WEBVIEW_MESSAGES . SWITCH_DEVICE : deviceSelectionService . setCurrentActiveDevice ( message . text . active_device ) ; killProcessIfRunning ( ) ; break ; default : vscode . window . showInformationMessage ( CONSTANTS . ERROR . UNEXPECTED_MESSAGE ) ; break ; } } , undefined , context . subscriptions ) ; activeEditorListener = utils . addVisibleTextEditorCallback ( currentPanel , context ) ; } currentPanel . onDidDispose ( ( ) => { currentPanel = undefined ; if ( debuggerCommunicationService . getCurrentDebuggerServer ( ) ) { debuggerCommunicationService . getCurrentDebuggerServer ( ) . setWebview ( undefined ) ; } killProcessIfRunning ( ) ; if ( firstTimeClosed ) { vscode . window . showInformationMessage ( CONSTANTS . INFO . FIRST_TIME_WEBVIEW ) ; firstTimeClosed = false ; } } , undefined , context . subscriptions ) ; } sendCurrentDeviceMessage ( currentPanel ) ; } ; const openSimulator = vscode . commands . registerCommand ( "deviceSimulatorExpress.common.openSimulator" , async ( ) => { const chosen_device = await vscode . window . showQuickPick ( Object . values ( CONSTANTS . DEVICE_NAME_FORMAL ) ) ; if ( ! chosen_device ) { utils . logToOutputChannel ( outChannel , CONSTANTS . INFO . NO_DEVICE_CHOSEN_TO_SIMULATE_TO , true ) ; return ; } const device = formalNameToNickNameMapping [ chosen_device ] ; deviceSelectionService . setCurrentActiveDevice ( device ) ; const telemetryEvents = telemetryHandlerService . getTelemetryEventsForOpenSimulator ( device ) ; telemetryAI . trackFeatureUsage ( telemetryEvents . openSimulatorTelemetryEvent ) ; telemetryAI . runWithLatencyMeasure ( openWebview , telemetryEvents . openSimulatorPerformanceTelemetryEvent ) ; } ) ; const gettingStartedOpen = vscode . commands . registerCommand ( "deviceSimulatorExpress.common.gettingStarted" , ( ) => { telemetryAI . trackFeatureUsage ( TelemetryEventName . COMMAND_GETTING_STARTED ) ; webviewService . openTutorialPanel ( ) ; } ) ; const openTemplateFile = ( template ) => { const fileName = template ; const filePath = __dirname + path . sep + "templates" + path . sep + fileName ; const file = fs . readFileSync ( filePath , "utf8" ) ; const showNewFilePopup = vscode . workspace . getConfiguration ( ) . get ( CONFIG . SHOW_NEW_FILE_POPUP ) ; if ( showNewFilePopup && template === CONSTANTS . TEMPLATE . CPX ) { vscode . window . showInformationMessage ( CONSTANTS . INFO . NEW_FILE , DialogResponses . DONT_SHOW , DialogResponses . EXAMPLE_CODE , DialogResponses . TUTORIALS ) . then ( ( selection ) => { if ( selection === DialogResponses . DONT_SHOW ) { vscode . workspace . getConfiguration ( ) . update ( CONFIG . SHOW_NEW_FILE_POPUP , false ) ; telemetryAI . trackFeatureUsage ( TelemetryEventName . CPX_CLICK_DIALOG_DONT_SHOW ) ; } else if ( selection === DialogResponses . EXAMPLE_CODE ) { open ( CONSTANTS . LINKS . EXAMPLE_CODE ) ; telemetryAI . trackFeatureUsage ( TelemetryEventName . CPX_CLICK_DIALOG_EXAMPLE_CODE ) ; } else if ( selection === DialogResponses . TUTORIALS ) { const okAction = ( ) => { open ( CONSTANTS . LINKS . TUTORIALS ) ; telemetryAI . trackFeatureUsage ( TelemetryEventName . CPX_CLICK_DIALOG_TUTORIALS ) ; } ; utils . showPrivacyModal ( okAction , CONSTANTS . INFO . THIRD_PARTY_WEBSITE_ADAFRUIT ) ; } } ) ; } vscode . workspace . openTextDocument ( { content : file , language : LANGUAGE_VARS . PYTHON . ID , } ) . then ( ( template ) => { vscode . window . showTextDocument ( template , 1 , false ) . then ( ( ) => { openWebview ( ) ; } ) ; } ) , ( error ) => { telemetryHandlerService . handleNewFileErrorTelemetry ( ) ; console . error ( `Failed to open a new text document:  ${ error }` ) ; } ; } ; const newFile = vscode . commands . registerCommand ( "deviceSimulatorExpress.common.newFile" , async ( ) => { const chosen_device = await vscode . window . showQuickPick ( Object . values ( CONSTANTS . DEVICE_NAME_FORMAL ) ) ; if ( ! chosen_device ) { utils . logToOutputChannel ( outChannel , CONSTANTS . INFO . NO_DEVICE_CHOSEN_FOR_NEW_FILE , true ) ; return ; } const device = formalNameToNickNameMapping [ chosen_device ] ; deviceSelectionService . setCurrentActiveDevice ( device ) ; const deviceToTemplateMapping = { [ CONSTANTS . DEVICE_NAME . CPX ] : CONSTANTS . TEMPLATE . CPX , [ CONSTANTS . DEVICE_NAME . MICROBIT ] : CONSTANTS . TEMPLATE . MICROBIT , [ CONSTANTS . DEVICE_NAME . CLUE ] : CONSTANTS . TEMPLATE . CLUE , } ; const templateFile = deviceToTemplateMapping [ device ] ; const telemetryEvents = telemetryHandlerService . getTelemetryEventsForNewFile ( device ) ; telemetryAI . trackFeatureUsage ( telemetryEvents . newFileTelemetryEvent ) ; telemetryAI . runWithLatencyMeasure ( ( ) => openTemplateFile ( templateFile ) , telemetryEvents . newFilePerformanceTelemetryEvent ) ; } ) ; const installDependencies = vscode . commands . registerCommand ( "deviceSimulatorExpress.common.installDependencies" , async ( ) => { pythonExecutablePath = await setupService . setupEnv ( context , true ) ; telemetryAI . trackFeatureUsage ( TelemetryEventName . COMMAND_INSTALL_EXTENSION_DEPENDENCIES ) ; } ) ; const killProcessIfRunning = ( ) => { if ( childProcess !== undefined ) { if ( currentPanel ) { currentPanel . webview . postMessage ( { command : "reset-state" , active_device : deviceSelectionService . getCurrentActiveDevice ( ) , } ) ; } childProcess . kill ( ) ; childProcess = undefined ; } } ; const runSimulatorCommand = async ( ) => { pythonProcessDataBuffer = [ ] ; if ( inDebugMode ) { vscode . window . showErrorMessage ( CONSTANTS . ERROR . DEBUGGING_SESSION_IN_PROGESS ) ; return ; } if ( shouldShowRunCodePopup ) { const shouldExitCommand = await vscode . window . showWarningMessage ( CONSTANTS . WARNING . ACCEPT_AND_RUN , DialogResponses . ACCEPT_AND_RUN , DialogResponses . CANCEL ) . then ( ( selection ) => { let hasAccepted = true ; if ( selection === DialogResponses . ACCEPT_AND_RUN ) { shouldShowRunCodePopup = false ; hasAccepted = false ; } return hasAccepted ; } ) ; if ( shouldExitCommand ) { return ; } } openWebview ( ) ; if ( ! currentPanel ) { return ; } console . info ( CONSTANTS . INFO . RUNNING_CODE ) ; utils . logToOutputChannel ( outChannel , CONSTANTS . INFO . DEPLOY_SIMULATOR , true ) ; killProcessIfRunning ( ) ; await fileSelectionService . updateCurrentFileFromEditor ( vscode . window . activeTextEditor ) ; if ( fileSelectionService . getCurrentFileAbsPath ( ) === "" ) { utils . logToOutputChannel ( outChannel , CONSTANTS . ERROR . NO_FILE_TO_RUN , true ) ; vscode . window . showErrorMessage ( CONSTANTS . ERROR . NO_FILE_TO_RUN , DialogResponses . MESSAGE_UNDERSTOOD ) ; } else { await fileSelectionService . getCurrentTextDocument ( ) . save ( ) ; if ( ! fileSelectionService . getCurrentTextDocument ( ) . fileName . endsWith ( LANGUAGE_VARS . PYTHON . FILE_ENDS ) ) { utils . logToOutputChannel ( outChannel , CONSTANTS . ERROR . NO_FILE_TO_RUN , true ) ; return ; } utils . logToOutputChannel ( outChannel , CONSTANTS . INFO . FILE_SELECTED ( fileSelectionService . getCurrentFileAbsPath ( ) ) ) ; currentPanel . webview . postMessage ( { command : "activate-play" , active_device : deviceSelectionService . getCurrentActiveDevice ( ) , } ) ; const args = [ utils . getPathToScript ( context , CONSTANTS . FILESYSTEM . OUTPUT_DIRECTORY , HELPER_FILES . PROCESS_USER_CODE_PY ) , fileSelectionService . getCurrentFileAbsPath ( ) , JSON . stringify ( { enable_telemetry : utils . getTelemetryState ( ) } ) , ] ; childProcess = cp . spawn ( pythonExecutablePath , args ) ; let dataFromTheProcess = "" ; let oldMessage = "" ; childProcess . stdout . on ( "data" , data => { dataFromTheProcess = data . toString ( ) ; if ( currentPanel ) { const processedData = pythonProcessDataBuffer . join ( "" ) . concat ( dataFromTheProcess ) ; pythonProcessDataBuffer = [ ] ; processedData . split ( "\0" ) . forEach ( message => { if ( currentPanel && message . length > 0 && message !== oldMessage ) { oldMessage = message ; let messageToWebview ; try { messageToWebview = JSON . parse ( message ) ; switch ( messageToWebview . type ) { case "state" : const messageData = JSON . parse ( messageToWebview . data ) ; if ( messageData . device_name === deviceSelectionService . getCurrentActiveDevice ( ) ) { messagingService . sendMessageToWebview ( VSCODE_MESSAGES_TO_WEBVIEW . SET_STATE , messageData ) ; } break ; case "print" : console . log ( `Process print statement output = ${ messageToWebview . data }` ) ; utils . logToOutputChannel ( outChannel , `[PRINT] ${ messageToWebview . data }` ) ; break ; default : console . log ( `Non-state JSON output from the process : ${ messageToWebview }` ) ; break ; } } catch ( err ) { if ( err instanceof SyntaxError ) { pythonProcessDataBuffer . push ( message ) ; } else { console . log ( `Errored output: ${ messageToWebview }` ) ; } } } } ) ; } } ) ; childProcess . stderr . on ( "data" , data => { console . error ( `Error from the Python process through stderr: ${ data }` ) ; telemetryAI . trackFeatureUsage ( TelemetryEventName . ERROR_PYTHON_PROCESS ) ; utils . logToOutputChannel ( outChannel , CONSTANTS . ERROR . STDERR ( data ) , true ) ; if ( currentPanel ) { currentPanel . webview . postMessage ( { active_device : deviceSelectionService . getCurrentActiveDevice ( ) , command : "reset-state" , } ) ; } } ) ; childProcess . on ( "end" , ( code ) => { console . info ( `Command execution exited with code: ${ code }` ) ; } ) ; } } ; const runSimulator = vscode . commands . registerCommand ( "deviceSimulatorExpress.common.runSimulator" , ( ) => { telemetryAI . trackFeatureUsage ( TelemetryEventName . COMMAND_RUN_PALETTE ) ; runSimulatorCommand ( ) ; } ) ; const deployCode = async ( device ) => { utils . logToOutputChannel ( outChannel , CONSTANTS . INFO . DEPLOY_DEVICE , true ) ; await fileSelectionService . updateCurrentFileFromEditor ( vscode . window . activeTextEditor ) ; if ( fileSelectionService . getCurrentFileAbsPath ( ) === "" ) { utils . logToOutputChannel ( outChannel , CONSTANTS . ERROR . NO_FILE_TO_DEPLOY , true ) ; vscode . window . showErrorMessage ( CONSTANTS . ERROR . NO_FILE_TO_DEPLOY , DialogResponses . MESSAGE_UNDERSTOOD ) ; } else { await fileSelectionService . getCurrentTextDocument ( ) . save ( ) ; utils . logToOutputChannel ( outChannel , CONSTANTS . INFO . FILE_SELECTED ( fileSelectionService . getCurrentFileAbsPath ( ) ) ) ; const deviceProcess = cp . spawn ( pythonExecutablePath , [ utils . getPathToScript ( context , CONSTANTS . FILESYSTEM . OUTPUT_DIRECTORY , HELPER_FILES . DEVICE_PY ) , device , fileSelectionService . getCurrentFileAbsPath ( ) , ] ) ; let dataFromTheProcess = "" ; deviceProcess . stdout . on ( "data" , data => { dataFromTheProcess = data . toString ( ) ; let messageToWebview ; try { messageToWebview = JSON . parse ( dataFromTheProcess ) ; if ( messageToWebview . type === "complete" ) { utils . logToOutputChannel ( outChannel , CONSTANTS . INFO . DEPLOY_SUCCESS , true ) ; } telemetryHandlerService . handleDeployToDeviceFinishedTelemetry ( messageToWebview , device ) ; } catch ( err ) { console . log ( `Non-JSON output from the process :  ${ dataFromTheProcess }` ) ; } } ) ; deviceProcess . stderr . on ( "data" , data => { telemetryHandlerService . handleDeployToDeviceErrorTelemetry ( data , device ) ; console . error ( `Error from the Python device process through stderr: ${ data }` ) ; utils . logToOutputChannel ( outChannel , `[ERROR] ${ data } \n` , true ) ; } ) ; deviceProcess . on ( "end" , ( code ) => { console . info ( `Command execution exited with code: ${ code }` ) ; } ) ; } } ; const deployToDevice = vscode . commands . registerCommand ( "deviceSimulatorExpress.common.deployToDevice" , async ( ) => { const chosen_device = await vscode . window . showQuickPick ( Object . values ( CONSTANTS . DEVICE_NAME_FORMAL ) ) ; if ( ! chosen_device ) { utils . logToOutputChannel ( outChannel , CONSTANTS . INFO . NO_DEVICE_CHOSEN_TO_DEPLOY_TO , true ) ; return ; } const device = formalNameToNickNameMapping [ chosen_device ] ; const telemetryEvents = telemetryHandlerService . getTelemetryEventsForStartingDeployToDevice ( device ) ; telemetryAI . trackFeatureUsage ( telemetryEvents . deployTelemetryEvent ) ; telemetryAI . runWithLatencyMeasure ( ( ) => { deployCode ( device ) ; } , telemetryEvents . deployPerformanceTelemetryEvent ) ; } ) ; let serialMonitor ; if ( configFileCreated ) { serialMonitor = SerialMonitor . getInstance ( ) ; context . subscriptions . push ( serialMonitor ) ; } const selectSerialPort = vscode . commands . registerCommand ( "deviceSimulatorExpress.common.selectSerialPort" , ( ) => { if ( serialMonitor ) { telemetryAI . runWithLatencyMeasure ( ( ) => { serialMonitor . selectSerialPort ( null , null ) ; } , TelemetryEventName . COMMAND_SERIAL_MONITOR_CHOOSE_PORT ) ; } else { vscode . window . showErrorMessage ( CONSTANTS . ERROR . NO_FOLDER_OPENED ) ; console . info ( "Serial monitor is not defined." ) ; } } ) ; const openSerialMonitor = vscode . commands . registerCommand ( "deviceSimulatorExpress.common.openSerialMonitor" , ( ) => { if ( serialMonitor ) { telemetryAI . runWithLatencyMeasure ( serialMonitor . openSerialMonitor . bind ( serialMonitor ) , TelemetryEventName . COMMAND_SERIAL_MONITOR_OPEN ) ; } else { vscode . window . showErrorMessage ( CONSTANTS . ERROR . NO_FOLDER_OPENED ) ; console . info ( "Serial monitor is not defined." ) ; } } ) ; const changeBaudRate = vscode . commands . registerCommand ( "deviceSimulatorExpress.common.changeBaudRate" , ( ) => { if ( serialMonitor ) { telemetryAI . runWithLatencyMeasure ( serialMonitor . changeBaudRate . bind ( serialMonitor ) , TelemetryEventName . COMMAND_SERIAL_MONITOR_BAUD_RATE ) ; } else { vscode . window . showErrorMessage ( CONSTANTS . ERROR . NO_FOLDER_OPENED ) ; console . info ( "Serial monitor is not defined." ) ; } } ) ; const closeSerialMonitor = vscode . commands . registerCommand ( "deviceSimulatorExpress.common.closeSerialMonitor" , ( port , showWarning = true ) => { if ( serialMonitor ) { telemetryAI . runWithLatencyMeasure ( ( ) => { serialMonitor . closeSerialMonitor ( port , showWarning ) ; } , TelemetryEventName . COMMAND_SERIAL_MONITOR_CLOSE ) ; } else { vscode . window . showErrorMessage ( CONSTANTS . ERROR . NO_FOLDER_OPENED ) ; console . info ( "Serial monitor is not defined." ) ; } } ) ; const showReleaseNote = vscode . commands . registerCommand ( "deviceSimulatorExpress." , ( port , showWarning = true ) => { if ( serialMonitor ) { telemetryAI . runWithLatencyMeasure ( ( ) => { serialMonitor . closeSerialMonitor ( port , showWarning ) ; } , TelemetryEventName . COMMAND_SERIAL_MONITOR_CLOSE ) ; } else { vscode . window . showErrorMessage ( CONSTANTS . ERROR . NO_FOLDER_OPENED ) ; console . info ( "Serial monitor is not defined." ) ; } } ) ; UsbDetector . getInstance ( ) . initialize ( context . extensionPath ) ; UsbDetector . getInstance ( ) . startListening ( ) ; if ( CPXWorkspace . rootPath && ( utils . fileExistsSync ( path . join ( CPXWorkspace . rootPath , CPX_CONFIG_FILE ) ) || vscode . window . activeTextEditor ) ) { ( ( ) => { if ( ! SerialMonitor . getInstance ( ) . initialized ) { SerialMonitor . getInstance ( ) . initialize ( ) ; } } ) ( ) ; } const simulatorDebugConfiguration = new SimulatorDebugConfigurationProvider ( utils . getPathToScript ( context , "out/" , "debug_user_code.py" ) ) ; const debugAdapterFactory = new DebugAdapterFactory ( vscode . debug . activeDebugSession , messagingService , debuggerCommunicationService ) ; vscode . debug . registerDebugAdapterTrackerFactory ( LANGUAGE_VARS . PYTHON . ID , debugAdapterFactory ) ; const debugSessionsStarted = vscode . debug . onDidStartDebugSession ( ( ) => { if ( simulatorDebugConfiguration . deviceSimulatorExpressDebug ) { killProcessIfRunning ( ) ; inDebugMode = true ; try { if ( debuggerCommunicationService . getCurrentDebuggerServer ( ) ) { debuggerCommunicationService . resetCurrentDebuggerServer ( ) ; } debuggerCommunicationService . setCurrentDebuggerServer ( new DebuggerCommunicationServer ( currentPanel , utils . getServerPortConfig ( ) , deviceSelectionService ) ) ; telemetryHandlerService . handleDebuggerTelemetry ( ) ; openWebview ( ) ; if ( currentPanel ) { debuggerCommunicationService . getCurrentDebuggerServer ( ) . setWebview ( currentPanel ) ; currentPanel . webview . postMessage ( { active_device : deviceSelectionService . getCurrentActiveDevice ( ) , command : "activate-play" , } ) ; } } catch ( err ) { if ( err . message === SERVER_INFO . ERROR_CODE_INIT_SERVER ) { console . error ( `Error trying to init the server on port ${ utils . getServerPortConfig ( ) }` ) ; telemetryHandlerService . handleDebuggerFailTelemetry ( ) ; vscode . window . showErrorMessage ( CONSTANTS . ERROR . DEBUGGER_SERVER_INIT_FAILED ( utils . getServerPortConfig ( ) ) ) ; } } } } ) ; const debugSessionStopped = vscode . debug . onDidTerminateDebugSession ( ( ) => { if ( simulatorDebugConfiguration . deviceSimulatorExpressDebug ) { inDebugMode = false ; simulatorDebugConfiguration . deviceSimulatorExpressDebug = false ; if ( debuggerCommunicationService . getCurrentDebuggerServer ( ) ) { debuggerCommunicationService . resetCurrentDebuggerServer ( ) ; } if ( currentPanel ) { currentPanel . webview . postMessage ( { command : "reset-state" , active_device : deviceSelectionService . getCurrentActiveDevice ( ) , } ) ; } } } ) ; const configsChanged = vscode . workspace . onDidChangeConfiguration ( async ( ) => { if ( utils . checkConfig ( CONFIG . CONFIG_ENV_ON_SWITCH ) ) { pythonExecutablePath = await setupService . setupEnv ( context ) ; } } ) ; context . subscriptions . push ( installDependencies , runSimulator , changeBaudRate , closeSerialMonitor , deployToDevice , newFile , openSimulator , openSerialMonitor , selectSerialPort , gettingStartedOpen , vscode . debug . registerDebugConfigurationProvider ( CONSTANTS . DEBUG_CONFIGURATION_TYPE , simulatorDebugConfiguration ) , debugSessionsStarted , debugSessionStopped , configsChanged ) ; } const updatePythonExtraPaths = ( ) => { updateConfigLists ( "python.autoComplete.extraPaths" , [ __dirname , path . join ( __dirname , CONSTANTS . FILESYSTEM . MICROPYTHON_DIRECTORY ) , path . join ( __dirname , CONSTANTS . FILESYSTEM . CLUE ) , path . join ( __dirname , CONSTANTS . FILESYSTEM . BASE_CIRCUITPYTHON ) , ] , vscode . ConfigurationTarget . Global ) ; } ; const updatePylintArgs = ( context ) => { const outPath = utils . createEscapedPath ( context . extensionPath , CONSTANTS . FILESYSTEM . OUTPUT_DIRECTORY ) ; const micropythonPath = utils . createEscapedPath ( context . extensionPath , CONSTANTS . FILESYSTEM . OUTPUT_DIRECTORY , CONSTANTS . FILESYSTEM . MICROPYTHON_DIRECTORY ) ; const cluePath = utils . createEscapedPath ( context . extensionPath , CONSTANTS . FILESYSTEM . OUTPUT_DIRECTORY , CONSTANTS . FILESYSTEM . CLUE ) ; const baseCircuitPythonPath = utils . createEscapedPath ( context . extensionPath , CONSTANTS . FILESYSTEM . OUTPUT_DIRECTORY , CONSTANTS . FILESYSTEM . BASE_CIRCUITPYTHON ) ; updateConfigLists ( "python.linting.pylintArgs" , [ "--init-hook" , `import sys; sys.path.extend([\"${ outPath }\",\"${ micropythonPath }\",\"${ cluePath }\",\"${ baseCircuitPythonPath }\"])` , ] , vscode . ConfigurationTarget . Workspace ) ; } ; const updateConfigLists = ( section , newItems , scope ) => { const currentExtraItems = vscode . workspace . getConfiguration ( ) . get ( section ) || [ ] ; const extraItemsSet = new Set ( currentExtraItems . concat ( newItems ) ) ; vscode . workspace . getConfiguration ( ) . update ( section , Array . from ( extraItemsSet ) , scope ) ; } ; export async function deactivate ( ) { const monitor = SerialMonitor . getInstance ( ) ; await monitor . closeSerialMonitor ( null , false ) ; UsbDetector . getInstance ( ) . stopListening ( ) ; }