Skip to content

Commit 5471379

Browse files
authored
Support debugging newly imported files (microsoft#6776)
* Force save on debug * Add test and news entry * Remove unnecessary spread
1 parent a6330b1 commit 5471379

6 files changed

Lines changed: 118 additions & 31 deletions

File tree

news/2 Fixes/6738.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Debugging an untitled file causes an error 'Untitled-1 cannot be opened'.

package-lock.json

Lines changed: 23 additions & 23 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/client/datascience/interactive-window/interactiveWindow.ts

Lines changed: 32 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -188,9 +188,38 @@ export class InteractiveWindow extends WebViewHost<IInteractiveWindowMapping> im
188188
return this.submitCode(code, file, line, undefined, editor, false);
189189
}
190190

191-
public debugCode(code: string, file: string, line: number, editor?: TextEditor): Promise<boolean> {
192-
// Call the internal method.
193-
return this.submitCode(code, file, line, undefined, editor, true);
191+
public async debugCode(code: string, file: string, line: number, editor?: TextEditor): Promise<boolean> {
192+
let saved = true;
193+
// Make sure the file is saved before debugging
194+
const doc = this.documentManager.textDocuments.find(d => d.fileName === file);
195+
if (doc && doc.isUntitled) {
196+
// Before we start, get the list of documents
197+
const beforeSave = [...this.documentManager.textDocuments];
198+
199+
saved = await doc.save();
200+
201+
// If that worked, we have to open the new document. It should be
202+
// the new entry in the list
203+
if (saved) {
204+
const diff = this.documentManager.textDocuments.filter(f => beforeSave.indexOf(f) === -1);
205+
if (diff && diff.length > 0) {
206+
file = diff[0].fileName;
207+
208+
// Open the new document
209+
await this.documentManager.openTextDocument(file);
210+
211+
// Change the editor to the new file
212+
editor = this.documentManager.visibleTextEditors.find(e => e.document.fileName === file);
213+
}
214+
}
215+
}
216+
217+
// Call the internal method if we were able to save
218+
if (saved) {
219+
return this.submitCode(code, file, line, undefined, editor, true);
220+
}
221+
222+
return false;
194223
}
195224

196225
// tslint:disable-next-line: no-any no-empty cyclomatic-complexity max-func-body-length

src/test/datascience/debugger.functional.test.tsx

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ import { MockDebuggerService } from './mockDebugService';
2828
import { MockDocumentManager } from './mockDocumentManager';
2929

3030
//import { asyncDump } from '../common/asyncDump';
31+
import { MockDocument } from './mockDocument';
3132
// tslint:disable-next-line:max-func-body-length no-any
3233
suite('DataScience Debugger tests', () => {
3334
const disposables: Disposable[] = [];
@@ -240,4 +241,42 @@ suite('DataScience Debugger tests', () => {
240241
await debugCell('#%%\nprint("bar")', undefined, undefined, true);
241242
}
242243
});
244+
245+
test('Debug temporary file', async () => {
246+
ioc.getSettings().datascience.stopOnFirstLineWhileDebugging = true;
247+
const code = '#%%\nprint("bar")';
248+
249+
// Create a dummy document with just this code
250+
const docManager = ioc.get<IDocumentManager>(IDocumentManager) as MockDocumentManager;
251+
const fileName = 'Untitled-1';
252+
docManager.addDocument(code, fileName);
253+
const mockDoc = docManager.textDocuments[0] as MockDocument;
254+
mockDoc.forceUntitled();
255+
256+
// Start the jupyter server
257+
const history = await getOrCreateInteractiveWindow();
258+
const expectedBreakLine = 2; // 2 because of the 'breakpoint()' that gets added
259+
260+
// Debug this code. We should either hit the breakpoint or stop on entry
261+
const resultPromise = getCellResults(ioc.wrapper!, 5, async () => {
262+
const breakPromise = createDeferred<void>();
263+
disposables.push(mockDebuggerService!.onBreakpointHit(() => breakPromise.resolve()));
264+
const done = history.debugCode(code, fileName, 0, docManager.activeTextEditor);
265+
await waitForPromise(Promise.race([done, breakPromise.promise]), 60000);
266+
assert.ok(breakPromise.resolved, 'Breakpoint event did not fire');
267+
assert.ok(!lastErrorMessage, `Error occurred ${lastErrorMessage}`);
268+
const stackTrace = await mockDebuggerService!.getStackTrace();
269+
assert.ok(stackTrace, 'Stack trace not computable');
270+
assert.ok(stackTrace!.body.stackFrames.length >= 1, 'Not enough frames');
271+
assert.equal(stackTrace!.body.stackFrames[0].line, expectedBreakLine, 'Stopped on wrong line number');
272+
assert.equal(stackTrace!.body.stackFrames[0].source!.path, path.join(EXTENSION_ROOT_DIR, 'baz.py'), 'Stopped on wrong file name. Name should have been saved');
273+
// Verify break location
274+
await mockDebuggerService!.continue();
275+
});
276+
277+
const cellResults = await resultPromise;
278+
assert.ok(cellResults, 'No cell results after finishing debugging');
279+
await history.dispose();
280+
});
281+
243282
});

src/test/datascience/mockDocument.ts

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -55,11 +55,20 @@ export class MockDocument implements TextDocument {
5555
private _version: number = 0;
5656
private _lines: MockLine[] = [];
5757
private _contents: string = '';
58+
private _isUntitled = false;
59+
private _isDirty = false;
60+
private _onSave: (doc: TextDocument) => Promise<boolean>;
5861

59-
constructor(contents: string, fileName: string) {
62+
constructor(contents: string, fileName: string, onSave: (doc: TextDocument) => Promise<boolean>) {
6063
this._uri = Uri.file(fileName);
6164
this._contents = contents;
6265
this._lines = this.createLines();
66+
this._onSave = onSave;
67+
}
68+
69+
public forceUntitled(): void {
70+
this._isUntitled = true;
71+
this._isDirty = true;
6372
}
6473

6574
public get uri(): Uri {
@@ -70,7 +79,7 @@ export class MockDocument implements TextDocument {
7079
}
7180

7281
public get isUntitled(): boolean {
73-
return true;
82+
return this._isUntitled;
7483
}
7584
public get languageId(): string {
7685
return 'python';
@@ -79,13 +88,13 @@ export class MockDocument implements TextDocument {
7988
return this._version;
8089
}
8190
public get isDirty(): boolean {
82-
return true;
91+
return this._isDirty;
8392
}
8493
public get isClosed(): boolean {
8594
return false;
8695
}
8796
public save(): Thenable<boolean> {
88-
return Promise.resolve(true);
97+
return this._onSave(this);
8998
}
9099
public get eol(): EndOfLine {
91100
return EndOfLine.LF;

src/test/datascience/mockDocumentManager.ts

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
// Copyright (c) Microsoft Corporation. All rights reserved.
22
// Licensed under the MIT License.
33
'use strict';
4+
import * as path from 'path';
45
import {
56
DecorationRenderOptions,
67
Event,
@@ -20,8 +21,10 @@ import {
2021
} from 'vscode';
2122

2223
import { IDocumentManager } from '../../client/common/application/types';
24+
import { EXTENSION_ROOT_DIR } from '../../client/constants';
2325
import { MockDocument } from './mockDocument';
2426
import { MockEditor } from './mockTextEditor';
27+
2528
// tslint:disable:no-any no-http-string no-multiline-string max-func-body-length
2629

2730
export class MockDocumentManager implements IDocumentManager {
@@ -81,7 +84,7 @@ export class MockDocumentManager implements IDocumentManager {
8184
}
8285

8386
public addDocument(code: string, file: string) {
84-
const mockDoc = new MockDocument(code, file);
87+
const mockDoc = new MockDocument(code, file, this.saveDocument);
8588
this.textDocuments.push(mockDoc);
8689
}
8790

@@ -118,4 +121,10 @@ export class MockDocumentManager implements IDocumentManager {
118121
}
119122
throw new Error('No documents in MockDocumentManager');
120123
}
124+
125+
private saveDocument = (doc: TextDocument): Promise<boolean> => {
126+
// Create a new document with the contents of the doc passed in
127+
this.addDocument(doc.getText(), path.join(EXTENSION_ROOT_DIR, 'baz.py'));
128+
return Promise.resolve(true);
129+
}
121130
}

0 commit comments

Comments
 (0)