Skip to content

Commit af38cb8

Browse files
authored
feat: skip files while debugging (#874)
* Add support for justMyCode where ignored files are stepInto-ed. Small refactor of log point evaluation code. * Changelog and Readme. * Renamed justMyCode to skipFiles same as javascript debugger. * Add support for pause and stepping with the same command that started the skipping process. * Extract matching function and tests.
1 parent 622c3e4 commit af38cb8

File tree

6 files changed

+98
-17
lines changed

6 files changed

+98
-17
lines changed

CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,10 @@ All notable changes to this project will be documented in this file.
44

55
The format is based on [Keep a Changelog](http://keepachangelog.com/) and this project adheres to [Semantic Versioning](http://semver.org/).
66

7+
## [1.30.0]
8+
9+
- Add skipFiles launch setting to skip over specified file patterns.
10+
711
## [1.29.1]
812

913
- Fix for env configuration check that sometimes causes an error.

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,7 @@ More general information on debugging with VS Code can be found on https://code.
7676
- `pathMappings`: A list of server paths mapping to the local source paths on your machine, see "Remote Host Debugging" below
7777
- `log`: Whether to log all communication between VS Code and the adapter to the debug console. See _Troubleshooting_ further down.
7878
- `ignore`: An optional array of glob patterns that errors should be ignored from (for example `**/vendor/**/*.php`)
79+
- `skipFiles`: An array of glob patterns, to skip when debugging. Star patterns and negations are allowed, for example, `**/vendor/**` or `!**/vendor/my-module/**`.
7980
- `maxConnections`: Accept only this number of parallel debugging sessions. Additional connections will be dropped and their execution will continue without debugging.
8081
- `proxy`: DBGp Proxy settings
8182
- `enable`: To enable proxy registration set to `true` (default is `false).

package.json

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -270,6 +270,14 @@
270270
"**/vendor/**/*.php"
271271
]
272272
},
273+
"skipFiles": {
274+
"type": "array",
275+
"items": "string",
276+
"description": "An array of glob patterns, to skip when debugging. Star patterns and negations are allowed, for example, `[\"**/vendor/**\", \"!**/vendor/my-module/**\"]`",
277+
"default": [
278+
"**/vendor/**"
279+
]
280+
},
273281
"log": {
274282
"type": "boolean",
275283
"description": "If true, will log all communication between VS Code and the adapter"

src/paths.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import * as url from 'url'
33
import * as path from 'path'
44
import { decode } from 'urlencode'
55
import RelateUrl from 'relateurl'
6+
import minimatch from 'minimatch'
67

78
/**
89
* Options to make sure that RelateUrl only outputs relative URLs and performs not other "smart" modifications.
@@ -153,3 +154,8 @@ export function isSameUri(clientUri: string, debuggerUri: string): boolean {
153154
return debuggerUri === clientUri
154155
}
155156
}
157+
158+
export function isPositiveMatchInGlobs(path: string, globs: string[]): boolean {
159+
const f = globs.find(glob => minimatch(path, glob.charAt(0) == '!' ? glob.substring(1) : glob))
160+
return f !== undefined && f.charAt(0) !== '!'
161+
}

src/phpDebug.ts

Lines changed: 61 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ import * as path from 'path'
99
import * as util from 'util'
1010
import * as fs from 'fs'
1111
import { Terminal } from './terminal'
12-
import { convertClientPathToDebugger, convertDebuggerPathToClient } from './paths'
12+
import { convertClientPathToDebugger, convertDebuggerPathToClient, isPositiveMatchInGlobs } from './paths'
1313
import minimatch from 'minimatch'
1414
import { BreakpointManager, BreakpointAdapter } from './breakpoints'
1515
import * as semver from 'semver'
@@ -76,6 +76,8 @@ export interface LaunchRequestArguments extends VSCodeDebugProtocol.LaunchReques
7676
log?: boolean
7777
/** Array of glob patterns that errors should be ignored from */
7878
ignore?: string[]
79+
/** Array of glob patterns that debugger should not step in */
80+
skipFiles?: string[]
7981
/** Xdebug configuration */
8082
xdebugSettings?: { [featureName: string]: string | number }
8183
/** proxy connection configuration */
@@ -165,6 +167,9 @@ class PhpDebugSession extends vscode.DebugSession {
165167
/** A flag to indicate that the adapter has already processed the stopOnEntry step request */
166168
private _hasStoppedOnEntry = false
167169

170+
/** A map from Xdebug connection id to state of skipping files */
171+
private _skippingFiles = new Map<number, boolean>()
172+
168173
/** Breakpoint Manager to map VS Code to Xdebug breakpoints */
169174
private _breakpointManager = new BreakpointManager()
170175

@@ -566,6 +571,7 @@ class PhpDebugSession extends vscode.DebugSession {
566571
this._connections.delete(connection.id)
567572
this._statuses.delete(connection)
568573
this._breakpointAdapters.delete(connection)
574+
this._skippingFiles.delete(connection.id)
569575
}
570576
}
571577

@@ -676,27 +682,43 @@ class PhpDebugSession extends vscode.DebugSession {
676682
stoppedEventReason = 'entry'
677683
this._hasStoppedOnEntry = true
678684
} else if (response.command.startsWith('step')) {
685+
await this._processLogPoints(response)
686+
// check just my code
687+
if (
688+
this._args.skipFiles &&
689+
isPositiveMatchInGlobs(
690+
convertDebuggerPathToClient(response.fileUri).replace(/\\/g, '/'),
691+
this._args.skipFiles
692+
)
693+
) {
694+
if (!this._skippingFiles.has(connection.id)) {
695+
this._skippingFiles.set(connection.id, true)
696+
}
697+
if (this._skippingFiles.get(connection.id)) {
698+
let stepResponse
699+
switch (response.command) {
700+
case 'step_out':
701+
stepResponse = await connection.sendStepOutCommand()
702+
break
703+
case 'step_over':
704+
stepResponse = await connection.sendStepOverCommand()
705+
break
706+
default:
707+
stepResponse = await connection.sendStepIntoCommand()
708+
}
709+
await this._checkStatus(stepResponse)
710+
return
711+
}
712+
this._skippingFiles.delete(connection.id)
713+
}
679714
stoppedEventReason = 'step'
680715
} else {
681-
stoppedEventReason = 'breakpoint'
682-
}
683-
// Check for log points
684-
if (this._logPointManager.hasLogPoint(response.fileUri, response.line)) {
685-
const logMessage = await this._logPointManager.resolveExpressions(
686-
response.fileUri,
687-
response.line,
688-
async (expr: string): Promise<string> => {
689-
const evaluated = await connection.sendEvalCommand(expr)
690-
return formatPropertyValue(evaluated.result)
691-
}
692-
)
693-
694-
this.sendEvent(new vscode.OutputEvent(logMessage + '\n', 'console'))
695-
if (stoppedEventReason === 'breakpoint') {
716+
if (await this._processLogPoints(response)) {
696717
const responseCommand = await connection.sendRunCommand()
697718
await this._checkStatus(responseCommand)
698719
return
699720
}
721+
stoppedEventReason = 'breakpoint'
700722
}
701723
const event: VSCodeDebugProtocol.StoppedEvent = new vscode.StoppedEvent(
702724
stoppedEventReason,
@@ -708,6 +730,24 @@ class PhpDebugSession extends vscode.DebugSession {
708730
}
709731
}
710732

733+
private async _processLogPoints(response: xdebug.StatusResponse): Promise<boolean> {
734+
const connection = response.connection
735+
if (this._logPointManager.hasLogPoint(response.fileUri, response.line)) {
736+
const logMessage = await this._logPointManager.resolveExpressions(
737+
response.fileUri,
738+
response.line,
739+
async (expr: string): Promise<string> => {
740+
const evaluated = await connection.sendEvalCommand(expr)
741+
return formatPropertyValue(evaluated.result)
742+
}
743+
)
744+
745+
this.sendEvent(new vscode.OutputEvent(logMessage + '\n', 'console'))
746+
return true
747+
}
748+
return false
749+
}
750+
711751
/** Logs all requests before dispatching */
712752
protected dispatchRequest(request: VSCodeDebugProtocol.Request): void {
713753
if (this._args?.log) {
@@ -1298,6 +1338,11 @@ class PhpDebugSession extends vscode.DebugSession {
12981338
response: VSCodeDebugProtocol.PauseResponse,
12991339
args: VSCodeDebugProtocol.PauseArguments
13001340
): void {
1341+
if (this._skippingFiles.has(args.threadId)) {
1342+
this._skippingFiles.set(args.threadId, false)
1343+
this.sendResponse(response)
1344+
return
1345+
}
13011346
this.sendErrorResponse(response, new Error('Pausing the execution is not supported by Xdebug'))
13021347
}
13031348

src/test/paths.ts

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { isSameUri, convertClientPathToDebugger, convertDebuggerPathToClient } from '../paths'
1+
import { isSameUri, convertClientPathToDebugger, convertDebuggerPathToClient, isPositiveMatchInGlobs } from '../paths'
22
import * as assert from 'assert'
33
import { describe, it } from 'mocha'
44

@@ -308,4 +308,21 @@ describe('paths', () => {
308308
})
309309
})
310310
})
311+
describe('isPositiveMatchInGlobs', () => {
312+
it('should not match empty globs', () => {
313+
assert.equal(isPositiveMatchInGlobs('/test/test.php', []), false)
314+
})
315+
it('should match positive globs', () => {
316+
assert.equal(isPositiveMatchInGlobs('/test/test.php', ['**/test/**']), true)
317+
})
318+
it('should not match positive globs', () => {
319+
assert.equal(isPositiveMatchInGlobs('/test/test.php', ['**/not_test/**']), false)
320+
})
321+
it('should match negative globs', () => {
322+
assert.equal(isPositiveMatchInGlobs('/test/test.php', ['!**/test.php', '**/test/**']), false)
323+
})
324+
it('should not match negative globs', () => {
325+
assert.equal(isPositiveMatchInGlobs('/test/test.php', ['!**/not_test/test.php', '**/test/**']), true)
326+
})
327+
})
311328
})

0 commit comments

Comments
 (0)