Skip to content

Commit

Permalink
feat: DBGp stream redirect support (#968)
Browse files Browse the repository at this point in the history
* feat: DBGp stream redirect support

* Test stream

* Expose stdout stream setting
  • Loading branch information
zobo authored Jul 15, 2024
1 parent 9be8a99 commit 9d25348
Show file tree
Hide file tree
Showing 4 changed files with 72 additions and 1 deletion.
11 changes: 11 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -366,6 +366,17 @@
"xdebugCloudToken": {
"type": "string",
"description": "Xdebug Could token"
},
"stream": {
"type": "object",
"description": "Xdebug stream settings",
"properties": {
"stdout": {
"type": "number",
"description": "Redirect stdout stream: 0 (disable), 1 (copy), 2 (redirect)",
"default": 0
}
}
}
}
}
Expand Down
13 changes: 13 additions & 0 deletions src/phpDebug.ts
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,10 @@ export interface LaunchRequestArguments extends VSCodeDebugProtocol.LaunchReques
maxConnections?: number
/** Xdebug cloud token */
xdebugCloudToken?: string
/** Xdebug stream settings */
stream?: {
stdout?: 0 | 1 | 2
}

// CLI options

Expand Down Expand Up @@ -566,6 +570,15 @@ class PhpDebugSession extends vscode.DebugSession {
throw new Error(`Error applying xdebugSettings: ${String(error instanceof Error ? error.message : error)}`)
}

const stdout =
this._args.stream?.stdout === undefined ? (this._args.externalConsole ? 1 : 0) : this._args.stream.stdout
if (stdout) {
await connection.sendStdout(stdout)
connection.on('stream', (stream: xdebug.Stream) =>
this.sendEvent(new vscode.OutputEvent(stream.value, 'stdout'))
)
}

this.sendEvent(new vscode.ThreadEvent('started', connection.id))

// wait for all breakpoints
Expand Down
17 changes: 16 additions & 1 deletion src/test/adapter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@ import { DebugClient } from '@vscode/debugadapter-testsupport'
import { DebugProtocol } from '@vscode/debugprotocol'
import * as semver from 'semver'
import * as net from 'net'
import { describe, it, beforeEach, afterEach } from 'mocha'
import * as childProcess from 'child_process'
import { describe, it, beforeEach, afterEach, after } from 'mocha'
chai.use(chaiAsPromised)
const assert = chai.assert

Expand Down Expand Up @@ -832,6 +833,20 @@ describe('PHP Debug Adapter', () => {
})
})

describe('stream tests', () => {
const program = path.join(TEST_PROJECT, 'output.php')

it('listen with externalConsole', async () => {
// this is how we can currently turn on stdout redirect
await Promise.all([client.launch({ stream: { stdout: '1' } }), client.configurationSequence()])

const script = childProcess.spawn('php', [program])
after(() => script.kill())
await client.assertOutput('stdout', 'stdout output 1')
await client.assertOutput('stdout', 'stdout output 2')
})
})

describe('special adapter tests', () => {
it('max connections', async () => {
await Promise.all([client.launch({ maxConnections: 1, log: true }), client.configurationSequence()])
Expand Down
32 changes: 32 additions & 0 deletions src/xdebugConnection.ts
Original file line number Diff line number Diff line change
Expand Up @@ -179,6 +179,24 @@ export class UserNotify extends Notify {
}
}

export class Stream {
/** Type of stream */
type: string
/** Data of the stream */
value: string

/** Constructs a stream object from an XML node from a Xdebug response */
constructor(document: XMLDocument) {
this.type = document.documentElement.getAttribute('type')!
const encoding = document.documentElement.getAttribute('encoding')
if (encoding) {
this.value = iconv.encode(document.documentElement.textContent!, encoding).toString()
} else {
this.value = document.documentElement.textContent!
}
}
}

export type BreakpointType = 'line' | 'call' | 'return' | 'exception' | 'conditional' | 'watch'
export type BreakpointState = 'enabled' | 'disabled'
export type BreakpointResolved = 'resolved' | 'unresolved'
Expand Down Expand Up @@ -745,6 +763,7 @@ export declare interface Connection extends DbgpConnection {
on(event: 'log', listener: (text: string) => void): this
on(event: 'notify_user', listener: (notify: UserNotify) => void): this
on(event: 'notify_breakpoint_resolved', listener: (notify: BreakpointResolvedNotify) => void): this
on(event: 'stream', listener: (stream: Stream) => void): this
}

/**
Expand Down Expand Up @@ -809,6 +828,9 @@ export class Connection extends DbgpConnection {
} else if (response.documentElement.nodeName === 'notify') {
const n = Notify.fromXml(response, this)
this.emit('notify_' + n.name, n)
} else if (response.documentElement.nodeName === 'stream') {
const s = new Stream(response)
this.emit('stream', s)
} else {
const transactionId = parseInt(response.documentElement.getAttribute('transaction_id')!)
if (this._pendingCommands.has(transactionId)) {
Expand Down Expand Up @@ -1115,4 +1137,14 @@ export class Connection extends DbgpConnection {
public async sendEvalCommand(expression: string): Promise<EvalResponse> {
return new EvalResponse(await this._enqueueCommand('eval', undefined, expression), this)
}

// ------------------------------ stream ----------------------------------------

public async sendStdout(mode: 0 | 1 | 2): Promise<Response> {
return new Response(await this._enqueueCommand('stdout', `-c ${mode}`), this)
}

public async sendStderr(mode: 0 | 1 | 2): Promise<Response> {
return new Response(await this._enqueueCommand('stderr', `-c ${mode}`), this)
}
}

0 comments on commit 9d25348

Please sign in to comment.