Skip to content

Commit 11dd074

Browse files
Drop dedent-on-enter for "return" statements. (#6939)
(for #6813) We'll work on adding support back in #6564.
1 parent 10ab7e7 commit 11dd074

File tree

8 files changed

+447
-31
lines changed

8 files changed

+447
-31
lines changed

news/2 Fixes/6813.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
Drop dedent-on-enter for "return" statements. It will be addressed in:
2+
https://github.com/microsoft/vscode-python/issues/6564

src/client/common/utils/regexp.ts

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
// Copyright (c) Microsoft Corporation. All rights reserved.
2+
// Licensed under the MIT License.
3+
4+
'use strict';
5+
6+
/* Generate a RegExp from a "verbose" pattern.
7+
*
8+
* All whitespace in the pattern is removed, including newlines. This
9+
* allows the pattern to be much more readable by allowing it to span
10+
* multiple lines and to separate tokens with insignificant whitespace.
11+
* The functionality is similar to the VERBOSE ("x") flag in Python's
12+
* regular expressions.
13+
*
14+
* Note that significant whitespace in the pattern must be explicitly
15+
* indicated by "\s". Also, unlike with regular expression literals,
16+
* backslashes must be escaped. Conversely, forward slashes do not
17+
* need to be escaped.
18+
*/
19+
export function verboseRegExp(pattern: string): RegExp {
20+
pattern = pattern.replace(/\s+?/g, '');
21+
return RegExp(pattern);
22+
}

src/client/extension.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ import { registerTypes as appRegisterTypes } from './application/serviceRegistry
4040
import { IApplicationDiagnostics } from './application/types';
4141
import { DebugService } from './common/application/debugService';
4242
import { IApplicationShell, ICommandManager, IWorkspaceService } from './common/application/types';
43-
import { Commands, isTestExecution, PYTHON, STANDARD_OUTPUT_CHANNEL } from './common/constants';
43+
import { Commands, isTestExecution, PYTHON, PYTHON_LANGUAGE, STANDARD_OUTPUT_CHANNEL } from './common/constants';
4444
import { registerTypes as registerDotNetTypes } from './common/dotnet/serviceRegistry';
4545
import { registerTypes as installerRegisterTypes } from './common/installer/serviceRegistry';
4646
import { traceError } from './common/logger';
@@ -85,7 +85,7 @@ import { registerTypes as interpretersRegisterTypes } from './interpreter/servic
8585
import { ServiceContainer } from './ioc/container';
8686
import { ServiceManager } from './ioc/serviceManager';
8787
import { IServiceContainer, IServiceManager } from './ioc/types';
88-
import { setLanguageConfiguration } from './language/languageConfiguration';
88+
import { getLanguageConfiguration } from './language/languageConfiguration';
8989
import { LinterCommands } from './linters/linterCommands';
9090
import { registerTypes as lintersRegisterTypes } from './linters/serviceRegistry';
9191
import { ILintingEngine } from './linters/types';
@@ -176,7 +176,7 @@ async function activateUnsafe(context: ExtensionContext): Promise<IExtensionApi>
176176
const linterProvider = new LinterProvider(context, serviceManager);
177177
context.subscriptions.push(linterProvider);
178178

179-
setLanguageConfiguration();
179+
languages.setLanguageConfiguration(PYTHON_LANGUAGE, getLanguageConfiguration());
180180

181181
if (pythonSettings && pythonSettings.formatting && pythonSettings.formatting.provider !== 'internalConsole') {
182182
const formatProvider = new PythonFormattingEditProvider(context, serviceContainer);

src/client/language/languageConfiguration.ts

Lines changed: 95 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -2,33 +2,111 @@
22
// Licensed under the MIT License.
33
'use strict';
44

5-
import { IndentAction, languages } from 'vscode';
6-
import { PYTHON_LANGUAGE } from '../common/constants';
5+
import { IndentAction, LanguageConfiguration } from 'vscode';
6+
import { verboseRegExp } from '../common/utils/regexp';
77

8-
export const MULTILINE_SEPARATOR_INDENT_REGEX = /^(?!\s+\\)[^#\n]+\\$/;
8+
// tslint:disable:no-multiline-string
99

10-
export function setLanguageConfiguration() {
11-
// Enable indentAction
12-
languages.setLanguageConfiguration(PYTHON_LANGUAGE, {
10+
// tslint:disable-next-line:max-func-body-length
11+
export function getLanguageConfiguration(): LanguageConfiguration {
12+
return {
1313
onEnterRules: [
14+
// multi-line separator
1415
{
15-
beforeText: /^\s*(?:def|class|for|if|elif|else|while|try|with|finally|except|async)\b.*:\s*/,
16-
action: { indentAction: IndentAction.Indent }
17-
},
18-
{
19-
beforeText: MULTILINE_SEPARATOR_INDENT_REGEX,
20-
action: { indentAction: IndentAction.Indent }
16+
beforeText: verboseRegExp(`
17+
^
18+
(?! \\s+ \\\\ )
19+
[^#\n]+
20+
\\\\
21+
$
22+
`),
23+
action: {
24+
indentAction: IndentAction.Indent
25+
}
2126
},
27+
// continue comments
2228
{
2329
beforeText: /^\s*#.*/,
2430
afterText: /.+$/,
25-
action: { indentAction: IndentAction.None, appendText: '# ' }
31+
action: {
32+
indentAction: IndentAction.None,
33+
appendText: '# '
34+
}
35+
},
36+
// indent on enter (block-beginning statements)
37+
{
38+
/**
39+
* This does not handle all cases. However, it does handle nearly all usage.
40+
* Here's what it does not cover:
41+
* - the statement is split over multiple lines (and hence the ":" is on a different line)
42+
* - the code block is inlined (after the ":")
43+
* - there are multiple statements on the line (separated by semicolons)
44+
* Also note that `lambda` is purposefully excluded.
45+
*/
46+
beforeText: verboseRegExp(`
47+
^
48+
\\s*
49+
(?:
50+
(?:
51+
(?:
52+
class |
53+
def |
54+
async \\s+ def |
55+
except |
56+
for |
57+
if |
58+
elif |
59+
while |
60+
with
61+
)
62+
\\b .*
63+
) |
64+
else |
65+
try |
66+
finally
67+
)
68+
\\s*
69+
[:]
70+
\\s*
71+
(?: [#] .* )?
72+
$
73+
`),
74+
action: {
75+
indentAction: IndentAction.Indent
76+
}
2677
},
78+
// outdent on enter (block-ending statements)
2779
{
28-
beforeText: /^\s+(continue|break|return)\b.*/,
29-
afterText: /\s+$/,
30-
action: { indentAction: IndentAction.Outdent }
80+
beforeText: verboseRegExp(`
81+
^
82+
(?:
83+
(?:
84+
\\s*
85+
(?:
86+
pass |
87+
raise \\s+ [^#\\s] [^#]*
88+
)
89+
) |
90+
(?:
91+
\\s+
92+
(?:
93+
raise |
94+
break |
95+
continue
96+
)
97+
)
98+
)
99+
\\s*
100+
(?: [#] .* )?
101+
$
102+
`),
103+
action: {
104+
indentAction: IndentAction.Outdent
105+
}
31106
}
107+
// Note that we do not currently have an auto-dedent
108+
// solution for "elif", "else", "except", and "finally".
109+
// We had one but had to remove it (see issue #6886).
32110
]
33-
});
111+
};
34112
}
Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
// Copyright (c) Microsoft Corporation. All rights reserved.
2+
// Licensed under the MIT License.
3+
4+
'use strict';
5+
6+
// tslint:disable:no-multiline-string
7+
8+
import { expect } from 'chai';
9+
10+
import {
11+
verboseRegExp
12+
} from '../../../client/common/utils/regexp';
13+
14+
suite('Utils for regular expressions - verboseRegExp()', () => {
15+
test('whitespace removed in multiline pattern (example of typical usage)', () => {
16+
const regex = verboseRegExp(`
17+
^
18+
(?:
19+
spam \\b .*
20+
) |
21+
(?:
22+
eggs \\b .*
23+
)
24+
$
25+
`);
26+
27+
expect(regex.source).to.equal('^(?:spam\\b.*)|(?:eggs\\b.*)$', 'mismatch');
28+
});
29+
30+
const whitespaceTests = [
31+
['spam eggs', 'spameggs'],
32+
[`spam
33+
eggs`, 'spameggs'],
34+
// empty
35+
[' ', '(?:)'],
36+
[`
37+
`, '(?:)']
38+
];
39+
for (const [pat, expected] of whitespaceTests) {
40+
test(`whitespace removed ("${pat}")`, () => {
41+
const regex = verboseRegExp(pat);
42+
43+
expect(regex.source).to.equal(expected, 'mismatch');
44+
});
45+
}
46+
47+
const noopPatterns = [
48+
'^(?:spam\\b.*)$',
49+
'spam',
50+
'^spam$',
51+
'spam$',
52+
'^spam'
53+
];
54+
for (const pat of noopPatterns) {
55+
test(`pattern not changed ("${pat}")`, () => {
56+
const regex = verboseRegExp(pat);
57+
58+
expect(regex.source).to.equal(pat, 'mismatch');
59+
});
60+
}
61+
62+
const emptyPatterns = [
63+
'',
64+
`
65+
`,
66+
' '
67+
];
68+
for (const pat of emptyPatterns) {
69+
test(`no pattern ("${pat}")`, () => {
70+
const regex = verboseRegExp(pat);
71+
72+
expect(regex.source).to.equal('(?:)', 'mismatch');
73+
});
74+
}
75+
});

0 commit comments

Comments
 (0)