(VS Code Internal)
TerminalService.constructor()
-> this._initializePrimaryBackend()
TerminalService._initializePrimaryBackend()
-> reconnectedPromise.then(() =>
this._setConnected()
)
TerminalService._setConnected()
-> this.onDidChangeConnectionState.fire()
TerminalViewPane.renderBody()
-> this._terminalService.onDidChangeConnectionState(() =>
this._initializeTerminal()
)
TerminalViewPane._initializeTerminal()
-> this._terminalService.createTerminal()
TerminalService.createTerminal()
-> this._createTerminal()
TerminalService._createTerminal()
-> this._terminalGroupService.createGroup()
TerminalGroupService.createGroup()
-> this._instantiationService.createInstance(TerminalGroup)
...
TerminalGroup.constructor()
-> this.addInstance()
TerminalGroup.addInstance()
-> this._terminalInstanceService.createInstance()
TerminalInstanceService.createInstance()
-> this._instantiationService.createInstance(TerminalInstance)
...
TerminalInstance.constructor()
-> this._xtermReadyPromise.then(() =>
this._createProcess()
)
TerminalInstance._createProcess()
-> this._processManager.createProcess()
TerminalProcessManager.createProcess()
-> this._launchLocalProcess()
TerminalProcessManager._launchLocalProcess()
-> backend.createProcess()
LocalTerminalBackend.createProcess()
-> this._proxy.createProcess()
ProxyChannel.toService()
-> return new Proxy()
-> get('createProcess')
-> channel.call()
getDelayedChannel()
-> {call: () => promise.then(() =>
c.call()
)}
ChannelClient.getChannel()
-> {call: () =>
that.requestPromise()
}
ChannelClient.requestPromise()
-> new Promise(() =>
this.sendRequest()
)
ChannelClient.sendRequest()
-> this.send()
ChannelClient.send()
-> this.sendBuffer()
ChannelClient.sendBuffer()
-> this.protocol.send()
Protocol.send()
-> this.port.postMessage()
(pty host)
Event.fromNodeEventEmitter()
-> result.fire()
Emitter.fire()
-> this._deliverQueue()
Emitter._deliverQueue()
-> this._deliver()
Emitter._deliver()
-> listener.value()
ChannelServer.constructor()
-> this.protocol.onMessage(() =>
this.onRawMessage()
)
ChannelServer.onRawMessage()
-> this.onPromise()
ChannelServer.onPromise()
-> channel.call()
-> ProxyChannel.fromService()
-> new class {call: () =>
target.apply()
}
-> traceRpc()
-> descriptor[fnKey] = () =>
fn.apply()
-> PtyService.createProcess()
-> new TerminalProcess()
-> PtyService.createProcess()
-> new PersistentTerminalProcess()
-> PtyService.createProcess()
-> return id
ChannelServer.onPromise()
-> promise.then(() =>
this.sendResponse()
)
ChannelServer.sendResponse()
-> this.send()
ChannelServer.send()
-> this.sendBuffer()
ChannelServer.sendBuffer()
-> this.protocol.send()
Protocol.send()
-> this.port.postMessage()
...
(VS Code Internal)
LocalTerminalBackend.createProcess()
-> id = this._proxy.createProcess()
LocalTerminalBackend.createProcess()
-> new LocalPty()
LocalPty.constructor()
-> super()
LocalTerminalBackend.createProcess()
-> return pty
TerminalProcessManager._launchLocalProcess()
-> return backend.createProcess()
TerminalProcessManager.createProcess()
-> newProcess = this._launchLocalProcess()
TerminalProcessManager.createProcess()
-> newProcess.start()
LocalPty.start()
-> this._proxy.start()
...
(pty host)
traceRpc()
-> descriptor[fnKey] = () =>
fn.apply()
PtyService.start()
-> pty.start()
PersistentTerminalProcess.start()
-> this._terminalProcess.start()
TerminalProcess.start()
-> this.setupPtyProcess()
TerminalProcess.setupPtyProcess()
-> spawn()
Eventually there are 3 windows here: a window with a vscode
repository, a window with a vscode-js-debug
repository and a window with a nextjs
project.
After F5
in the first window the second window appears.
After F5
in the second window a new debug session is started:
name: Extension
request: launch
type: extensionHost (pwa-extensionHost)
It sends $startDASession
and $sendDAMessage
(command: launch
) to the debugger:
KeybindingsRegistry.registerCommandAndKeybindingRule({handler: () =>
debugService.startDebugging()
})
DebugService.startDebugging()
->this.createSession()
DebugService.createSession()
->this.doCreateSession()
DebugService.doCreateSession()
->this.launchOrAttachToSession()
DebugService.launchOrAttachToSession()
->session.initialize()
DebugSession.initialize()
->this.raw.start()
RawDebugSession.start()
->this.debugAdapter.startSession()
ExtensionHostDebugAdapter.startSession()
->this._proxy.$startDASession()
DebugService.launchOrAttachToSession()
->session.launchOrAttach()
DebugSession.launchOrAttach()
->this.raw.launchOrAttach()
RawDebugSession.launchOrAttach()
->this.send()
RawDebugSession.send()
->new Promise(() =>
this.debugAdapter.sendRequest()
)
AbstractDebugAdapter.sendRequest()
->this.internalSend()
AbstractDebugAdapter.internalSend()
->this.sendMessage()
ExtensionHostDebugAdapter.sendMessage()
->this._proxy.$sendDAMessage()
After the launch
request is processed (RawDebugSession.launchOrAttach()
-> this.send()
) the third window appears and apparently another instance of VS Code Internal is launched (for vscode-js-debug
).
Both instances of VS Code Internal receive an AttachSession
event. The second instance doesn't have the target session, so the handler does nothing. The first instance sends $startDASession
and $sendDAMessage
(command: attach
) to the debugger:
DebugService.constructor()
-> [this.extensionHostDebugService.onAttachSession(() =>
this.launchOrAttachToSession()
)
DebugService.launchOrAttachToSession()
->session.initialize()
DebugSession.initialize()
->this.raw.start()
RawDebugSession.start()
->this.debugAdapter.startSession()
ExtensionHostDebugAdapter.startSession()
->this._proxy.$startDASession()
DebugService.launchOrAttachToSession()
->session.launchOrAttach()
DebugSession.launchOrAttach()
->this.raw.launchOrAttach()
RawDebugSession.launchOrAttach()
->this.send()
RawDebugSession.send()
->new Promise(() =>
this.debugAdapter.sendRequest()
)
AbstractDebugAdapter.sendRequest()
->this.internalSend()
AbstractDebugAdapter.internalSend()
->this.sendMessage()
ExtensionHostDebugAdapter.sendMessage()
->this._proxy.$sendDAMessage()
Then a MainThreadDebugService.$startDebugging()
RPC call is received by the first instance:
Config:
name: Extension Host [0]
type: pwa-chrome
request: launch
Which creates a child debug session, sends $startDASession
and $sendDAMessage
(command: launch
) to the debugger:
MainThreadDebugService.$startDebugging()
->this.debugService.startDebugging()
DebugService.startDebugging()
->this.createSession()
DebugService.createSession()
->this.doCreateSession()
DebugService.doCreateSession()
->this.launchOrAttachToSession()
DebugService.launchOrAttachToSession()
->session.initialize()
DebugSession.initialize()
->this.raw.start()
RawDebugSession.start()
->this.debugAdapter.startSession()
ExtensionHostDebugAdapter.startSession()
->this._proxy.$startDASession()
DebugService.launchOrAttachToSession()
->session.launchOrAttach()
DebugSession.launchOrAttach()
->this.raw.launchOrAttach()
RawDebugSession.launchOrAttach()
->this.send()
RawDebugSession.send()
->new Promise(() =>
this.debugAdapter.sendRequest()
)
AbstractDebugAdapter.sendRequest()
->this.internalSend()
AbstractDebugAdapter.internalSend()
->this.sendMessage()
ExtensionHostDebugAdapter.sendMessage()
->this._proxy.$sendDAMessage()
Press F5
in the third window (the second VS Code Internal instance). This sends $startDASession
to vscode-js-debug
:
name: Next.js: debug server-side
type: node-terminal
request: launch
KeybindingsRegistry.registerCommandAndKeybindingRule({handler:
debugService.startDebugging()
})
DebugService.startDebugging()
->this.createSession()
DebugService.createSession()
->this.doCreateSession()
DebugService.doCreateSession()
->this.launchOrAttachSession()
DebugService.launchOrAttachToSession()
->session.initialize()
DebugSession.initialize()
->this.raw.start()
RawDebugSession.start()
->this.debugAdapter.startSession()
ExtensionHostDebugAdapter.startSession()
->this._proxy.$startDASession()
vscode-js-debug
receives it, starts a named pipe server and starts listening to requests:
ExtHostDebugServiceBase.$startDASession()
->this.getAdapterDescriptor()
ExtHostDebugServiceBase.getAdapterDescriptor()
->asPromise(() =>
adapterDescriptorFactory.createDebugAdapterDescriptor()
)
VSCodeSessionManager.createDebugAdapterDescriptor()
->this.sessionServerManager.createDebugServer()
ServerSessionManager.createDebugServer()
->this.createRootDebugServer()
ServerSessionManager.createRootDebugServer()
->this.innerCreateServer()
ServerSessionManager.innerCreateServer()
->new Promise(() => net.createServer().listen(pipe, () =>
resolve(s)
))
ServerSessionManager.innerCreateServer()
->() =>
sessionCreationFunc()
ServerSessionManager.createRootDebugServer()
->() =>
this.sessionManager.createNewRootSession()
SessionManager.createNewRootSession()
->root.createBinder()
RootSession.createBinder()
->new Binder()
Binder.constructor()
->dap.on('launch', ...)
Then we're back in the second VS Code Internal instance, which sends a launch
request to vscode-js-debug
:
DebugService.launchOrAttachToSession()
->session.launchOrAttach()
DebugSession.launchOrAttach()
->this.raw.launchOrAttach()
RawDebugSession.launchOrAttach()
->this.send()
RawDebugSession.send()
->new Promise(() =>
this.debugAdapter.sendRequest()
)
AbstractDebugAdapter.sendRequest()
->this.internalSend()
AbstractDebugAdapter.internalSend()
->this.sendMessage()
ExtensionHostDebugAdapter.sendMessage()
->this._proxy.$sendDAMessage()
Which vscode-js-debug
receives. In response it sends a npm run dev
command to MainThreadTerminalService
:
Binder.constructor()
->dap.on('launch', () =>
this.boot()
)
Binder.boot()
->this._boot()
Binder._boot()
->this.getLaunchers().map(() =>
this._launch()
)
Binder._launch()
->this.captureLaunch()
Binder.captureLaunch()
->launcher.launch()
NodeLauncherBase.launch()
->this._startServer()
NodeLauncherBase.launch()
->this.launchProgram()
TerminalNodeLauncher.launchProgram()
->terminal.sendText()
ExtHostTerminal.constructor()
->{sendText: () =>
this._proxy.$sendText()
}
MainThreadTerminalService
receives it and sends it to the pty host:
MainThreadTerminalService.$sendText()
->instance?.sendText()
TerminalInstance.sendText()
->this._processManager.write()
TerminalProcessManager.write()
->this._process.input()
LocalPty.input()
->this._proxy.input()
Pty host receives it and sends it to the shellIntegration-bash.sh
process:
PtyService.input()
->this._throwIfNoPty(id).input()
PersistentTerminalProcess.input()
->this._terminalProcess.input()
TerminalProcess.input()
->this._writeQueue.push(chunkInput(data).map())
TerminalProcess.input()
->this._startWrite()
TerminalProcess._startWrite()
->this._doWrite()
TerminalProcess._doWrite()
->this._ptyProcess!.write()
TerminalProcess._startWrite()
->setTimeout(() => this._startWrite())
TerminalProcess.setupPtyProcess()
-> new ChildProcessMonitor()
TerminalProcess._doWrite()
-> this._childProcessMonitor?.handleInput()
ChildProcessMonitor.handleInput()
-> this._refreshActive()
ChildProcessMonitor._refreshActive()
-> listProcesses()
listProcesses()
-> new Promise(() =>
exec('ps.sh')
)