Skip to content

Commit 55f5ca5

Browse files
authored
Enhancements to running code in a terminal (#1432)
Fixes #1207 Fixes #1316 Fixes #1349 Fixes #259
1 parent 2ba4e6c commit 55f5ca5

24 files changed

+270
-36
lines changed

news/1 Enhancements/1207.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Remove empty spaces from the selected text of the active editor when executing in a terminal.

news/1 Enhancements/1316.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Save the python file before running it in the terminal using the command/menu `Run Python File in Terminal`.

news/1 Enhancements/1349.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Add `Ctrl+Enter` keyboard shortcut for `Run Selection/Line in Python Terminal`.

news/2 Fixes/259.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Add blank lines to seprate blocks of indented code (function defs, classes, and the like) to ensure the code can be run within a Python interactive prompt.

package.json

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,12 @@
9494
"path": "./snippets/python.json"
9595
}
9696
],
97+
"keybindings":[
98+
{
99+
"command": "python.execSelectionInTerminal",
100+
"key": "ctrl+enter"
101+
}
102+
],
97103
"commands": [
98104
{
99105
"command": "python.sortImports",

src/client/common/configSettings.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ export class PythonSettings extends EventEmitter implements IPythonSettings {
3737
public formatting?: IFormattingSettings;
3838
public autoComplete?: IAutoCompleteSettings;
3939
public unitTest?: IUnitTestSettings;
40-
public terminal?: ITerminalSettings;
40+
public terminal!: ITerminalSettings;
4141
public sortImports?: ISortImportSettings;
4242
public workspaceSymbols?: IWorkspaceSymbolSettings;
4343
public disableInstallationChecks = false;

src/client/common/types.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -106,7 +106,7 @@ export interface IPythonSettings {
106106
readonly formatting?: IFormattingSettings;
107107
readonly unitTest?: IUnitTestSettings;
108108
readonly autoComplete?: IAutoCompleteSettings;
109-
readonly terminal?: ITerminalSettings;
109+
readonly terminal: ITerminalSettings;
110110
readonly sortImports?: ISortImportSettings;
111111
readonly workspaceSymbols?: IWorkspaceSymbolSettings;
112112
readonly envFile: string;

src/client/terminals/codeExecution/codeExecutionManager.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ export class CodeExecutionManager implements ICodeExecutionManager {
3535
if (!fileToExecute) {
3636
return;
3737
}
38+
await codeExecutionHelper.saveFileIfDirty(fileToExecute);
3839
const executionService = this.serviceContainer.get<ICodeExecutionService>(ICodeExecutionService, 'standard');
3940
await executionService.executeFile(fileToExecute);
4041
}
@@ -59,11 +60,11 @@ export class CodeExecutionManager implements ICodeExecutionManager {
5960
}
6061
const codeExecutionHelper = this.serviceContainer.get<ICodeExecutionHelper>(ICodeExecutionHelper);
6162
const codeToExecute = await codeExecutionHelper.getSelectedTextToExecute(activeEditor!);
62-
const normalizedCode = codeExecutionHelper.normalizeLines(codeToExecute!);
63+
const normalizedCode = await codeExecutionHelper.normalizeLines(codeToExecute!);
6364
if (!normalizedCode || normalizedCode.trim().length === 0) {
6465
return;
6566
}
6667

67-
await executionService.execute(codeToExecute!, activeEditor!.document.uri);
68+
await executionService.execute(normalizedCode, activeEditor!.document.uri);
6869
}
6970
}

src/client/terminals/codeExecution/djangoShellCodeExecution.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ export class DjangoShellCodeExecutionProvider extends TerminalCodeExecutionProvi
3232
public getReplCommandArgs(resource?: Uri): { command: string; args: string[] } {
3333
const pythonSettings = this.configurationService.getSettings(resource);
3434
const command = this.platformService.isWindows ? pythonSettings.pythonPath.replace(/\\/g, '/') : pythonSettings.pythonPath;
35-
const args = pythonSettings.terminal!.launchArgs.slice();
35+
const args = pythonSettings.terminal.launchArgs.slice();
3636

3737
const workspaceUri = resource ? this.workspace.getWorkspaceFolder(resource) : undefined;
3838
const defaultWorkspace = Array.isArray(this.workspace.workspaceFolders) && this.workspace.workspaceFolders.length > 0 ? this.workspace.workspaceFolders[0].uri.fsPath : '';

src/client/terminals/codeExecution/helper.ts

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

44
import { inject, injectable } from 'inversify';
5-
import { EOL } from 'os';
65
import { Range, TextEditor, Uri } from 'vscode';
76
import { IApplicationShell, IDocumentManager } from '../../common/application/types';
87
import { PythonLanguage } from '../../common/constants';
98
import '../../common/extensions';
9+
import { IServiceContainer } from '../../ioc/types';
1010
import { ICodeExecutionHelper } from '../types';
1111

1212
@injectable()
1313
export class CodeExecutionHelper implements ICodeExecutionHelper {
14-
constructor( @inject(IDocumentManager) private documentManager: IDocumentManager,
15-
@inject(IApplicationShell) private applicationShell: IApplicationShell) {
16-
14+
private readonly documentManager: IDocumentManager;
15+
private readonly applicationShell: IApplicationShell;
16+
constructor(@inject(IServiceContainer) serviceContainer: IServiceContainer) {
17+
this.documentManager = serviceContainer.get<IDocumentManager>(IDocumentManager);
18+
this.applicationShell = serviceContainer.get<IApplicationShell>(IApplicationShell);
1719
}
18-
public normalizeLines(code: string): string {
19-
const codeLines = code.splitLines({ trim: false, removeEmptyEntries: false });
20-
const codeLinesWithoutEmptyLines = codeLines.filter(line => line.trim().length > 0);
21-
return codeLinesWithoutEmptyLines.join(EOL);
20+
public async normalizeLines(code: string, resource?: Uri): Promise<string> {
21+
try {
22+
if (code.trim().length === 0) {
23+
return '';
24+
}
25+
const regex = /(\n)([ \t]*\r?\n)([ \t]+\S+)/gm;
26+
return code.replace(regex, (_, a, b, c) => {
27+
return `${a}${c}`;
28+
});
29+
} catch (ex) {
30+
console.error(ex, 'Python: Failed to normalize code for execution in terminal');
31+
return code;
32+
}
2233
}
2334

2435
public async getFileToExecute(): Promise<Uri | undefined> {
@@ -35,6 +46,9 @@ export class CodeExecutionHelper implements ICodeExecutionHelper {
3546
this.applicationShell.showErrorMessage('The active file is not a Python source file');
3647
return;
3748
}
49+
if (activeEditor.document.isDirty) {
50+
await activeEditor.document.save();
51+
}
3852
return activeEditor.document.uri;
3953
}
4054

@@ -53,4 +67,10 @@ export class CodeExecutionHelper implements ICodeExecutionHelper {
5367
}
5468
return code;
5569
}
70+
public async saveFileIfDirty(file: Uri): Promise<void> {
71+
const docs = this.documentManager.textDocuments.filter(d => d.uri.path === file.path);
72+
if (docs.length === 1 && docs[0].isDirty) {
73+
await docs[0].save();
74+
}
75+
}
5676
}

0 commit comments

Comments
 (0)