Skip to content

Commit b5e1e44

Browse files
authored
Ensure code is executed when last line of selected code is indented (microsoft#2222)
* Ensure code is executed when last line of selected code is indented * Tweak news entry * Address code review issues * Fix code review comments
1 parent 388e9c1 commit b5e1e44

4 files changed

Lines changed: 25 additions & 18 deletions

File tree

news/2 Fixes/2167.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Ensure code is executed when the last line of selected code is indented.

pythonFiles/normalizeForInterpreter.py

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -76,9 +76,15 @@ def normalize_lines(source):
7676
7777
"""
7878
lines = source.splitlines(False)
79+
# If we have two blank lines, then add two blank lines.
80+
# Do not trim the spaces, if we have blank lines with spaces, its possible
81+
# we have indented code.
82+
if (len(lines) > 1 and len(''.join(lines[-2:])) == 0) \
83+
or source.endswith(('\n\n', '\r\n\r\n')):
84+
trailing_newline = os.linesep * 2
7985
# Find out if we have any trailing blank lines
80-
if len(lines[-1].strip()) == 0 or source.endswith('\n'):
81-
trailing_newline = '\n'
86+
elif len(lines[-1].strip()) == 0 or source.endswith(('\n', '\r\n')):
87+
trailing_newline = os.linesep
8288
else:
8389
trailing_newline = ''
8490

@@ -95,7 +101,7 @@ def normalize_lines(source):
95101
# Step 2: Add blank lines between each global statement block.
96102
# A consequtive single lines blocks of code will be treated as a single statement,
97103
# just to ensure we do not unnecessarily add too many blank lines.
98-
source = '\n'.join(lines)
104+
source = os.linesep.join(lines)
99105
tokens = _tokenize(source)
100106
dedent_indexes = (spos[0] for (toknum, tokval, spos, epos, line) in tokens
101107
if toknum == token.DEDENT and _indent_size(line) == 0)
@@ -105,7 +111,7 @@ def normalize_lines(source):
105111
for line_number in filter(lambda x: x > 1, start_positions):
106112
lines.insert(line_number-1, '')
107113

108-
sys.stdout.write('\n'.join(lines) + trailing_newline)
114+
sys.stdout.write(os.linesep.join(lines) + trailing_newline)
109115
sys.stdout.flush()
110116

111117

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

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -80,11 +80,16 @@ suite('Terminal - Code Execution Helper', () => {
8080
const expectedCode = await fs.readFile(path.join(TEST_FILES_PATH, `sample${fileNameSuffix}_normalized.py`), 'utf8');
8181
await ensureBlankLinesAreRemoved(code, expectedCode);
8282
});
83-
// test(`Ensure blank lines are removed, including leading empty lines (${fileName})`, async () => {
84-
// const code = await fs.readFile(path.join(TEST_FILES_PATH, `${fileName}_raw.py`), 'utf8');
85-
// const expectedCode = await fs.readFile(path.join(TEST_FILES_PATH, `${fileName}_normalized.py`), 'utf8');
86-
// await ensureBlankLinesAreRemoved(['', '', ''].join(EOL) + EOL + code, expectedCode);
87-
// });
83+
test(`Ensure last two blank lines are preserved (Sample${fileNameSuffix})`, async () => {
84+
const code = await fs.readFile(path.join(TEST_FILES_PATH, `sample${fileNameSuffix}_raw.py`), 'utf8');
85+
const expectedCode = await fs.readFile(path.join(TEST_FILES_PATH, `sample${fileNameSuffix}_normalized.py`), 'utf8');
86+
await ensureBlankLinesAreRemoved(code + EOL, expectedCode + EOL);
87+
});
88+
test(`Ensure last two blank lines are preserved even if we have more than 2 trailing blank lines (Sample${fileNameSuffix})`, async () => {
89+
const code = await fs.readFile(path.join(TEST_FILES_PATH, `sample${fileNameSuffix}_raw.py`), 'utf8');
90+
const expectedCode = await fs.readFile(path.join(TEST_FILES_PATH, `sample${fileNameSuffix}_normalized.py`), 'utf8');
91+
await ensureBlankLinesAreRemoved(code + EOL + EOL + EOL + EOL, expectedCode + EOL);
92+
});
8893
});
8994
test('Display message if there\s no active file', async () => {
9095
documentManager.setup(doc => doc.activeTextEditor).returns(() => undefined);

src/test/terminals/codeExecution/terminalCodeExec.test.ts renamed to src/test/terminals/codeExecution/terminalCodeExec.unit.test.ts

Lines changed: 4 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,14 @@
11
// Copyright (c) Microsoft Corporation. All rights reserved.
22
// Licensed under the MIT License.
33

4-
// tslint:disable:no-multiline-string no-trailing-whitespace
4+
// tslint:disable:no-multiline-string no-trailing-whitespace max-func-body-length
55

66
import { expect } from 'chai';
77
import * as path from 'path';
88
import * as TypeMoq from 'typemoq';
99
import { Disposable, Uri, WorkspaceFolder } from 'vscode';
1010
import { ICommandManager, IDocumentManager, IWorkspaceService } from '../../../client/common/application/types';
11+
import { noop } from '../../../client/common/core.utils';
1112
import { IFileSystem, IPlatformService } from '../../../client/common/platform/types';
1213
import { ITerminalService, ITerminalServiceFactory } from '../../../client/common/terminal/types';
1314
import { IConfigurationService, IPythonSettings, ITerminalSettings } from '../../../client/common/types';
@@ -17,9 +18,7 @@ import { TerminalCodeExecutionProvider } from '../../../client/terminals/codeExe
1718
import { ICodeExecutionService } from '../../../client/terminals/types';
1819
import { PYTHON_PATH } from '../../common';
1920

20-
// tslint:disable-next-line:max-func-body-length
2121
suite('Terminal - Code Execution', () => {
22-
// tslint:disable-next-line:max-func-body-length
2322
['Terminal Execution', 'Repl Execution', 'Django Execution'].forEach(testSuiteName => {
2423
let terminalSettings: TypeMoq.IMock<ITerminalSettings>;
2524
let terminalService: TypeMoq.IMock<ITerminalService>;
@@ -46,7 +45,6 @@ suite('Terminal - Code Execution', () => {
4645
disposables = [];
4746
});
4847

49-
// tslint:disable-next-line:max-func-body-length
5048
setup(() => {
5149
terminalFactory = TypeMoq.Mock.ofType<ITerminalServiceFactory>();
5250
terminalSettings = TypeMoq.Mock.ofType<ITerminalSettings>();
@@ -76,8 +74,7 @@ suite('Terminal - Code Execution', () => {
7674
case 'Django Execution': {
7775
isDjangoRepl = true;
7876
workspace.setup(w => w.onDidChangeWorkspaceFolders(TypeMoq.It.isAny(), TypeMoq.It.isAny(), TypeMoq.It.isAny())).returns(() => {
79-
// tslint:disable-next-line:no-empty
80-
return { dispose: () => { } };
77+
return { dispose: noop };
8178
});
8279
executor = new DjangoShellCodeExecutionProvider(terminalFactory.object, configService.object, workspace.object, documentManager.object,
8380
platform.object, commandManager.object, fileSystem.object, disposables);
@@ -119,7 +116,6 @@ suite('Terminal - Code Execution', () => {
119116
});
120117
});
121118

122-
// tslint:disable-next-line:max-func-body-length
123119
suite(testSuiteName, () => {
124120
setup(() => {
125121
terminalFactory.setup(f => f.getTerminalService(TypeMoq.It.isAny(), TypeMoq.It.isAny())).returns(() => terminalService.object);
@@ -323,8 +319,7 @@ suite('Terminal - Code Execution', () => {
323319
terminalService.setup(t => t.onDidCloseTerminal(TypeMoq.It.isAny(), TypeMoq.It.isAny(), TypeMoq.It.isAny())).returns((callback => {
324320
closeTerminalCallback = callback;
325321
return {
326-
// tslint:disable-next-line:no-empty
327-
dispose: () => void 0
322+
dispose: noop
328323
};
329324
}));
330325

0 commit comments

Comments
 (0)