Skip to content

Commit 56d1912

Browse files
author
Kartik Raj
authored
Fix running Untitled files with the play button (#20955)
1 parent 96aa8f8 commit 56d1912

File tree

4 files changed

+39
-17
lines changed

4 files changed

+39
-17
lines changed

src/client/terminals/codeExecution/codeExecutionManager.ts

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -91,11 +91,14 @@ export class CodeExecutionManager implements ICodeExecutionManager {
9191
sendTelemetryEvent(EventName.EXECUTION_CODE, undefined, { scope: 'file', trigger });
9292
const codeExecutionHelper = this.serviceContainer.get<ICodeExecutionHelper>(ICodeExecutionHelper);
9393
file = file instanceof Uri ? file : undefined;
94-
const fileToExecute = file ? file : await codeExecutionHelper.getFileToExecute();
94+
let fileToExecute = file ? file : await codeExecutionHelper.getFileToExecute();
9595
if (!fileToExecute) {
9696
return;
9797
}
98-
await codeExecutionHelper.saveFileIfDirty(fileToExecute);
98+
const fileAfterSave = await codeExecutionHelper.saveFileIfDirty(fileToExecute);
99+
if (fileAfterSave) {
100+
fileToExecute = fileAfterSave;
101+
}
99102

100103
try {
101104
const contents = await this.fileSystem.readFile(fileToExecute.fsPath);

src/client/terminals/codeExecution/helper.ts

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import '../../common/extensions';
55
import { inject, injectable } from 'inversify';
66
import { l10n, Position, Range, TextEditor, Uri } from 'vscode';
77

8-
import { IApplicationShell, IDocumentManager } from '../../common/application/types';
8+
import { IApplicationShell, ICommandManager, IDocumentManager } from '../../common/application/types';
99
import { PYTHON_LANGUAGE } from '../../common/constants';
1010
import * as internalScripts from '../../common/process/internal/scripts';
1111
import { IProcessServiceFactory } from '../../common/process/types';
@@ -14,6 +14,7 @@ import { IInterpreterService } from '../../interpreter/contracts';
1414
import { IServiceContainer } from '../../ioc/types';
1515
import { ICodeExecutionHelper } from '../types';
1616
import { traceError } from '../../logging';
17+
import { Resource } from '../../common/types';
1718

1819
@injectable()
1920
export class CodeExecutionHelper implements ICodeExecutionHelper {
@@ -25,7 +26,7 @@ export class CodeExecutionHelper implements ICodeExecutionHelper {
2526

2627
private readonly interpreterService: IInterpreterService;
2728

28-
constructor(@inject(IServiceContainer) serviceContainer: IServiceContainer) {
29+
constructor(@inject(IServiceContainer) private readonly serviceContainer: IServiceContainer) {
2930
this.documentManager = serviceContainer.get<IDocumentManager>(IDocumentManager);
3031
this.applicationShell = serviceContainer.get<IApplicationShell>(IApplicationShell);
3132
this.processServiceFactory = serviceContainer.get<IProcessServiceFactory>(IProcessServiceFactory);
@@ -119,11 +120,17 @@ export class CodeExecutionHelper implements ICodeExecutionHelper {
119120
return code;
120121
}
121122

122-
public async saveFileIfDirty(file: Uri): Promise<void> {
123+
public async saveFileIfDirty(file: Uri): Promise<Resource> {
123124
const docs = this.documentManager.textDocuments.filter((d) => d.uri.path === file.path);
124125
if (docs.length === 1 && docs[0].isDirty) {
125-
await docs[0].save();
126+
const deferred = createDeferred<Uri>();
127+
this.documentManager.onDidSaveTextDocument((e) => deferred.resolve(e.uri));
128+
const commandManager = this.serviceContainer.get<ICommandManager>(ICommandManager);
129+
await commandManager.executeCommand('workbench.action.files.save', file);
130+
const savedFileUri = await deferred.promise;
131+
return savedFileUri;
126132
}
133+
return undefined;
127134
}
128135
}
129136

src/client/terminals/types.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
// Licensed under the MIT License.
33

44
import { Event, Terminal, TextEditor, Uri } from 'vscode';
5-
import { IDisposable } from '../common/types';
5+
import { IDisposable, Resource } from '../common/types';
66

77
export const ICodeExecutionService = Symbol('ICodeExecutionService');
88

@@ -17,7 +17,7 @@ export const ICodeExecutionHelper = Symbol('ICodeExecutionHelper');
1717
export interface ICodeExecutionHelper {
1818
normalizeLines(code: string): Promise<string>;
1919
getFileToExecute(): Promise<Uri | undefined>;
20-
saveFileIfDirty(file: Uri): Promise<void>;
20+
saveFileIfDirty(file: Uri): Promise<Resource>;
2121
getSelectedTextToExecute(textEditor: TextEditor): Promise<string | undefined>;
2222
}
2323

src/test/terminals/codeExecution/helper.test.ts

Lines changed: 21 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,8 @@ import * as fs from 'fs-extra';
88
import * as path from 'path';
99
import { SemVer } from 'semver';
1010
import * as TypeMoq from 'typemoq';
11-
import { Position, Range, Selection, TextDocument, TextEditor, TextLine, Uri } from 'vscode';
12-
import { IApplicationShell, IDocumentManager } from '../../../client/common/application/types';
11+
import { EventEmitter, Position, Range, Selection, TextDocument, TextEditor, TextLine, Uri } from 'vscode';
12+
import { IApplicationShell, ICommandManager, IDocumentManager } from '../../../client/common/application/types';
1313
import { EXTENSION_ROOT_DIR, PYTHON_LANGUAGE } from '../../../client/common/constants';
1414
import '../../../client/common/extensions';
1515
import { ProcessService } from '../../../client/common/process/proc';
@@ -37,6 +37,7 @@ suite('Terminal - Code Execution Helper', () => {
3737
let editor: TypeMoq.IMock<TextEditor>;
3838
let processService: TypeMoq.IMock<IProcessService>;
3939
let interpreterService: TypeMoq.IMock<IInterpreterService>;
40+
let commandManager: TypeMoq.IMock<ICommandManager>;
4041
const workingPython: PythonEnvironment = {
4142
path: PYTHON_PATH,
4243
version: new SemVer('3.6.6-final'),
@@ -49,6 +50,7 @@ suite('Terminal - Code Execution Helper', () => {
4950

5051
setup(() => {
5152
const serviceContainer = TypeMoq.Mock.ofType<IServiceContainer>();
53+
commandManager = TypeMoq.Mock.ofType<ICommandManager>();
5254
documentManager = TypeMoq.Mock.ofType<IDocumentManager>();
5355
applicationShell = TypeMoq.Mock.ofType<IApplicationShell>();
5456
const envVariablesProvider = TypeMoq.Mock.ofType<IEnvironmentVariablesProvider>();
@@ -79,6 +81,7 @@ suite('Terminal - Code Execution Helper', () => {
7981
serviceContainer
8082
.setup((c) => c.get(TypeMoq.It.isValue(IApplicationShell), TypeMoq.It.isAny()))
8183
.returns(() => applicationShell.object);
84+
serviceContainer.setup((c) => c.get(TypeMoq.It.isValue(ICommandManager))).returns(() => commandManager.object);
8285
serviceContainer
8386
.setup((c) => c.get(TypeMoq.It.isValue(IEnvironmentVariablesProvider), TypeMoq.It.isAny()))
8487
.returns(() => envVariablesProvider.object);
@@ -364,15 +367,24 @@ suite('Terminal - Code Execution Helper', () => {
364367
.setup((d) => d.textDocuments)
365368
.returns(() => [document.object])
366369
.verifiable(TypeMoq.Times.once());
367-
document.setup((doc) => doc.isUntitled).returns(() => false);
370+
const saveEmitter = new EventEmitter<TextDocument>();
371+
documentManager.setup((d) => d.onDidSaveTextDocument).returns(() => saveEmitter.event);
372+
document.setup((doc) => doc.isUntitled).returns(() => true);
368373
document.setup((doc) => doc.isDirty).returns(() => true);
369374
document.setup((doc) => doc.languageId).returns(() => PYTHON_LANGUAGE);
370-
const expectedUri = Uri.file('one.py');
371-
document.setup((doc) => doc.uri).returns(() => expectedUri);
372-
373-
await helper.saveFileIfDirty(expectedUri);
374-
documentManager.verifyAll();
375-
document.verify((doc) => doc.save(), TypeMoq.Times.once());
375+
const untitledUri = Uri.file('Untitled-1');
376+
document.setup((doc) => doc.uri).returns(() => untitledUri);
377+
const savedDocument = TypeMoq.Mock.ofType<TextDocument>();
378+
const expectedSavedUri = Uri.file('one.py');
379+
savedDocument.setup((doc) => doc.uri).returns(() => expectedSavedUri);
380+
commandManager
381+
.setup((c) => c.executeCommand('workbench.action.files.save', untitledUri))
382+
.callback(() => saveEmitter.fire(savedDocument.object))
383+
.returns(() => Promise.resolve());
384+
385+
const savedUri = await helper.saveFileIfDirty(untitledUri);
386+
387+
expect(savedUri?.fsPath).to.be.equal(expectedSavedUri.fsPath);
376388
});
377389

378390
test('File will be not saved if file is not dirty', async () => {

0 commit comments

Comments
 (0)