Skip to content

Commit 2b61db7

Browse files
Copilotsheremet-va
andcommitted
Fix stack trace parsing and enable feature by default
Co-authored-by: sheremet-va <[email protected]>
1 parent e07ec67 commit 2b61db7

File tree

5 files changed

+80
-20
lines changed

5 files changed

+80
-20
lines changed

README.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111
- **Run**, **debug**, and **watch** Vitest tests in Visual Studio Code.
1212
- **Coverage** support (requires VS Code >= 1.88)
1313
- An `@open` tag can be used when filtering tests, to only show the tests open in the editor.
14-
- **Inline console.log display**: Optionally show `console.log` output inline in the editor (enable via `vitest.showConsoleLogInline` setting)
14+
- **Inline console.log display**: Show `console.log` output inline in the editor next to the line that produced it (enabled by default, requires `printConsoleTrace: true` in Vitest config)
1515

1616
## Requirements
1717

@@ -100,7 +100,7 @@ These options are resolved relative to the [workspace file](https://code.visuals
100100
- `vitest.logLevel`: How verbose should the logger be in the "Output" channel. Default: `info`
101101
- `vitest.applyDiagnostic`: Show a squiggly line where the error was thrown. This also enables the error count in the File Tab. Default: `true`
102102
- `vitest.experimentalStaticAstCollect`: uses AST parses to collect tests instead of running files and collecting them at runtime. Default: `true`
103-
- `vitest.showConsoleLogInline`: Show `console.log` output inline in the editor next to the line that produced it. This can be useful for debugging. Default: `false`
103+
- `vitest.showConsoleLogInline`: Show `console.log` output inline in the editor next to the line that produced it. Note: This requires `printConsoleTrace: true` in your Vitest config to include stack trace information. Default: `true`
104104

105105
### Commands
106106

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -255,7 +255,7 @@
255255
"vitest.showConsoleLogInline": {
256256
"description": "Show console.log output inline in the editor next to the line that produced it.",
257257
"type": "boolean",
258-
"default": false,
258+
"default": true,
259259
"scope": "resource"
260260
}
261261
}

packages/extension/src/config.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,7 @@ export function getConfig(workspaceFolder?: WorkspaceFolder) {
7272
const debugOutFiles = get<string[]>('debugOutFiles', [])
7373
const applyDiagnostic = get<boolean>('applyDiagnostic', true)
7474
const ignoreWorkspace = get<boolean>('ignoreWorkspace', false) ?? false
75-
const showConsoleLogInline = get<boolean>('showConsoleLogInline', false)!
75+
const showConsoleLogInline = get<boolean>('showConsoleLogInline', true)!
7676

7777
return {
7878
env: get<null | Record<string, string>>('nodeEnv', null),

packages/extension/src/inlineConsoleLog.ts

Lines changed: 33 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -110,21 +110,41 @@ export class InlineConsoleLogManager extends vscode.Disposable {
110110
return null
111111
}
112112

113-
// Origin format is typically: file:line:column
114-
// Example: /path/to/file.ts:10:5
115-
const match = origin.match(/^(.+):(\d+):(\d+)$/)
116-
if (!match) {
117-
return null
118-
}
119-
120-
const [, file, lineStr] = match
121-
const line = Number.parseInt(lineStr, 10) - 1 // Convert to 0-based line number
122-
123-
if (Number.isNaN(line)) {
124-
return null
113+
// Origin is a stack trace string. We need to extract the file path and line number.
114+
// Stack trace formats vary but typically look like:
115+
// at functionName (file:///path/to/file.ts:10:5)
116+
// at /path/to/file.ts:10:5
117+
// at Object.<anonymous> (/path/to/file.ts:10:5)
118+
// We look for the first line that contains a file path with line:column
119+
120+
const lines = origin.split('\n')
121+
for (const line of lines) {
122+
// Match various stack trace formats
123+
// Handles: (file:///path/to/file.ts:10:5) or (/path/to/file.ts:10:5) or just /path/to/file.ts:10:5
124+
const match = line.match(/(?:file:\/\/)?([^():\s]+\.(?:ts|js|jsx|tsx|mjs|cjs|cts|mts)):(\d+):(\d+)/)
125+
if (match) {
126+
const [, file, lineStr] = match
127+
const lineNum = Number.parseInt(lineStr, 10) - 1 // Convert to 0-based line number
128+
129+
if (!Number.isNaN(lineNum) && lineNum >= 0) {
130+
// Clean up file:// protocol if present and decode URI components
131+
let cleanPath = file
132+
if (cleanPath.startsWith('file://')) {
133+
cleanPath = cleanPath.substring(7)
134+
}
135+
try {
136+
cleanPath = decodeURIComponent(cleanPath)
137+
}
138+
catch {
139+
// If decoding fails, use the original path
140+
}
141+
142+
return { file: cleanPath, line: lineNum }
143+
}
144+
}
125145
}
126146

127-
return { file, line }
147+
return null
128148
}
129149

130150
private updateDecorations(editor: vscode.TextEditor): void {

test/unit/inlineConsoleLog.test.ts

Lines changed: 43 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,11 @@ import { expect } from 'chai'
33
import { InlineConsoleLogManager } from '../../packages/extension/src/inlineConsoleLog'
44

55
describe('InlineConsoleLogManager', () => {
6-
it('correctly parses origin with file path', () => {
6+
it('correctly parses stack trace with file path', () => {
77
const manager = new InlineConsoleLogManager()
88
const log: UserConsoleLog = {
99
content: 'test message',
10-
origin: '/path/to/file.ts:10:5',
10+
origin: ' at Object.<anonymous> (/path/to/file.ts:10:5)',
1111
type: 'stdout',
1212
taskId: 'test-id',
1313
time: Date.now(),
@@ -25,6 +25,46 @@ describe('InlineConsoleLogManager', () => {
2525
manager.dispose()
2626
})
2727

28+
it('correctly parses stack trace with file:// protocol', () => {
29+
const manager = new InlineConsoleLogManager()
30+
const log: UserConsoleLog = {
31+
content: 'test message',
32+
origin: ' at functionName (file:///path/to/file.ts:10:5)',
33+
type: 'stdout',
34+
taskId: 'test-id',
35+
time: Date.now(),
36+
size: 12,
37+
}
38+
39+
// @ts-expect-error accessing private method for testing
40+
const result = manager.parseOrigin(log.origin)
41+
42+
expect(result).to.deep.equal({
43+
file: '/path/to/file.ts',
44+
line: 9, // 0-based
45+
})
46+
47+
manager.dispose()
48+
})
49+
50+
it('correctly parses multi-line stack trace', () => {
51+
const manager = new InlineConsoleLogManager()
52+
const stackTrace = ` at consoleLog (file:///path/to/helper.ts:5:3)
53+
at test (file:///path/to/file.ts:10:5)
54+
at Object.<anonymous> (file:///path/to/other.ts:20:3)`
55+
56+
// @ts-expect-error accessing private method for testing
57+
const result = manager.parseOrigin(stackTrace)
58+
59+
// Should parse the first valid line
60+
expect(result).to.deep.equal({
61+
file: '/path/to/helper.ts',
62+
line: 4, // 0-based
63+
})
64+
65+
manager.dispose()
66+
})
67+
2868
it('returns null for invalid origin', () => {
2969
const manager = new InlineConsoleLogManager()
3070

@@ -33,7 +73,7 @@ describe('InlineConsoleLogManager', () => {
3373
// @ts-expect-error accessing private method for testing
3474
expect(manager.parseOrigin('invalid')).to.be.null
3575
// @ts-expect-error accessing private method for testing
36-
expect(manager.parseOrigin('/path/to/file.ts')).to.be.null
76+
expect(manager.parseOrigin('no file path here')).to.be.null
3777

3878
manager.dispose()
3979
})

0 commit comments

Comments
 (0)