Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Drop dedent-on-enter for "return" statements. #6939

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
b8ae859
Revert "Revert "Add regex to dedent `else` and friends (#6497)" (#6945)"
ericsnowcurrently Aug 12, 2019
9b94f35
Copy IndentAction into the mock VSCode.
ericsnowcurrently Aug 1, 2019
319dd87
Test against getLanguageConfiguration().
ericsnowcurrently Aug 1, 2019
d1da292
language config formatting
ericsnowcurrently Aug 1, 2019
9add75c
Inline the regexes.
ericsnowcurrently Aug 1, 2019
465147f
Add a verboseRegExp() helper.
ericsnowcurrently Aug 1, 2019
e90bcbb
Use verbose regexes in the language config.
ericsnowcurrently Aug 1, 2019
09f4110
Make the regex tests a little easier to read/modify.
ericsnowcurrently Aug 1, 2019
cbbb419
Correct the break/continue/return regex.
ericsnowcurrently Aug 1, 2019
0ccf9cd
Fill in language config regex test coverage.
ericsnowcurrently Aug 12, 2019
02cee4b
Minor fixes to language config regex patterns.
ericsnowcurrently Aug 12, 2019
cc055fe
Drop support for indent-on-enter for "return *".
ericsnowcurrently Aug 12, 2019
c20ea1c
Drop indent-on-enter for "return" completely.
ericsnowcurrently Aug 12, 2019
1ff16dc
Drop an erroneous test.
ericsnowcurrently Aug 12, 2019
b630586
Add a NEWS entry.
ericsnowcurrently Aug 12, 2019
395442f
Add tests for verboseRegExp().
ericsnowcurrently Aug 12, 2019
dea0c9a
Move indentationRules back to onEnterRules.
ericsnowcurrently Aug 12, 2019
ad4a4be
Move a comment.
ericsnowcurrently Aug 12, 2019
15d1ff2
Add a doc comment for verboseRegExp().
ericsnowcurrently Aug 13, 2019
5632014
Clarify the objective of a test.
ericsnowcurrently Aug 13, 2019
611b126
Move the language configuration test suites into a parent suite.
ericsnowcurrently Aug 13, 2019
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions news/2 Fixes/6813.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
Drop dedent-on-enter for "return" statements. It will be addressed in:
https://github.com/microsoft/vscode-python/issues/6564
22 changes: 22 additions & 0 deletions src/client/common/utils/regexp.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.

'use strict';

/* Generate a RegExp from a "verbose" pattern.
*
* All whitespace in the pattern is removed, including newlines. This
* allows the pattern to be much more readable by allowing it to span
* multiple lines and to separate tokens with insignificant whitespace.
* The functionality is similar to the VERBOSE ("x") flag in Python's
* regular expressions.
*
* Note that significant whitespace in the pattern must be explicitly
* indicated by "\s". Also, unlike with regular expression literals,
* backslashes must be escaped. Conversely, forward slashes do not
* need to be escaped.
*/
export function verboseRegExp(pattern: string): RegExp {
ericsnowcurrently marked this conversation as resolved.
Show resolved Hide resolved
pattern = pattern.replace(/\s+?/g, '');
return RegExp(pattern);
}
6 changes: 3 additions & 3 deletions src/client/extension.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ import { registerTypes as appRegisterTypes } from './application/serviceRegistry
import { IApplicationDiagnostics } from './application/types';
import { DebugService } from './common/application/debugService';
import { IApplicationShell, ICommandManager, IWorkspaceService } from './common/application/types';
import { Commands, isTestExecution, PYTHON, STANDARD_OUTPUT_CHANNEL } from './common/constants';
import { Commands, isTestExecution, PYTHON, PYTHON_LANGUAGE, STANDARD_OUTPUT_CHANNEL } from './common/constants';
import { registerTypes as registerDotNetTypes } from './common/dotnet/serviceRegistry';
import { registerTypes as installerRegisterTypes } from './common/installer/serviceRegistry';
import { traceError } from './common/logger';
Expand Down Expand Up @@ -85,7 +85,7 @@ import { registerTypes as interpretersRegisterTypes } from './interpreter/servic
import { ServiceContainer } from './ioc/container';
import { ServiceManager } from './ioc/serviceManager';
import { IServiceContainer, IServiceManager } from './ioc/types';
import { setLanguageConfiguration } from './language/languageConfiguration';
import { getLanguageConfiguration } from './language/languageConfiguration';
import { LinterCommands } from './linters/linterCommands';
import { registerTypes as lintersRegisterTypes } from './linters/serviceRegistry';
import { ILintingEngine } from './linters/types';
Expand Down Expand Up @@ -176,7 +176,7 @@ async function activateUnsafe(context: ExtensionContext): Promise<IExtensionApi>
const linterProvider = new LinterProvider(context, serviceManager);
context.subscriptions.push(linterProvider);

setLanguageConfiguration();
languages.setLanguageConfiguration(PYTHON_LANGUAGE, getLanguageConfiguration());

if (pythonSettings && pythonSettings.formatting && pythonSettings.formatting.provider !== 'internalConsole') {
const formatProvider = new PythonFormattingEditProvider(context, serviceContainer);
Expand Down
112 changes: 95 additions & 17 deletions src/client/language/languageConfiguration.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,33 +2,111 @@
// Licensed under the MIT License.
'use strict';

import { IndentAction, languages } from 'vscode';
import { PYTHON_LANGUAGE } from '../common/constants';
import { IndentAction, LanguageConfiguration } from 'vscode';
import { verboseRegExp } from '../common/utils/regexp';

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

export function setLanguageConfiguration() {
// Enable indentAction
languages.setLanguageConfiguration(PYTHON_LANGUAGE, {
// tslint:disable-next-line:max-func-body-length
export function getLanguageConfiguration(): LanguageConfiguration {
return {
onEnterRules: [
// multi-line separator
{
beforeText: /^\s*(?:def|class|for|if|elif|else|while|try|with|finally|except|async)\b.*:\s*/,
action: { indentAction: IndentAction.Indent }
},
{
beforeText: MULTILINE_SEPARATOR_INDENT_REGEX,
action: { indentAction: IndentAction.Indent }
beforeText: verboseRegExp(`
^
(?! \\s+ \\\\ )
[^#\n]+
\\\\
$
`),
action: {
indentAction: IndentAction.Indent
}
},
// continue comments
{
beforeText: /^\s*#.*/,
afterText: /.+$/,
action: { indentAction: IndentAction.None, appendText: '# ' }
action: {
indentAction: IndentAction.None,
appendText: '# '
}
},
// indent on enter (block-beginning statements)
{
/**
* This does not handle all cases. However, it does handle nearly all usage.
* Here's what it does not cover:
* - the statement is split over multiple lines (and hence the ":" is on a different line)
* - the code block is inlined (after the ":")
* - there are multiple statements on the line (separated by semicolons)
* Also note that `lambda` is purposefully excluded.
*/
beforeText: verboseRegExp(`
^
\\s*
(?:
(?:
(?:
class |
def |
async \\s+ def |
except |
for |
if |
elif |
while |
with
)
\\b .*
) |
else |
try |
finally
)
\\s*
[:]
kimadeline marked this conversation as resolved.
Show resolved Hide resolved
\\s*
(?: [#] .* )?
kimadeline marked this conversation as resolved.
Show resolved Hide resolved
$
`),
action: {
indentAction: IndentAction.Indent
}
},
// outdent on enter (block-ending statements)
{
beforeText: /^\s+(continue|break|return)\b.*/,
afterText: /\s+$/,
action: { indentAction: IndentAction.Outdent }
beforeText: verboseRegExp(`
^
(?:
(?:
\\s*
(?:
pass |
raise \\s+ [^#\\s] [^#]*
kimadeline marked this conversation as resolved.
Show resolved Hide resolved
)
) |
(?:
\\s+
(?:
raise |
break |
continue
)
)
)
\\s*
(?: [#] .* )?
$
`),
action: {
indentAction: IndentAction.Outdent
}
}
// Note that we do not currently have an auto-dedent
// solution for "elif", "else", "except", and "finally".
// We had one but had to remove it (see issue #6886).
ericsnowcurrently marked this conversation as resolved.
Show resolved Hide resolved
]
});
};
}
75 changes: 75 additions & 0 deletions src/test/common/utils/regexp.unit.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.

'use strict';

// tslint:disable:no-multiline-string

import { expect } from 'chai';

import {
verboseRegExp
} from '../../../client/common/utils/regexp';

suite('Utils for regular expressions - verboseRegExp()', () => {
test('whitespace removed in multiline pattern (example of typical usage)', () => {
const regex = verboseRegExp(`
^
(?:
spam \\b .*
) |
(?:
eggs \\b .*
)
$
`);

expect(regex.source).to.equal('^(?:spam\\b.*)|(?:eggs\\b.*)$', 'mismatch');
});

const whitespaceTests = [
['spam eggs', 'spameggs'],
[`spam
eggs`, 'spameggs'],
// empty
[' ', '(?:)'],
[`
`, '(?:)']
];
for (const [pat, expected] of whitespaceTests) {
test(`whitespace removed ("${pat}")`, () => {
const regex = verboseRegExp(pat);

expect(regex.source).to.equal(expected, 'mismatch');
});
}

const noopPatterns = [
'^(?:spam\\b.*)$',
'spam',
'^spam$',
'spam$',
'^spam'
];
for (const pat of noopPatterns) {
test(`pattern not changed ("${pat}")`, () => {
const regex = verboseRegExp(pat);

expect(regex.source).to.equal(pat, 'mismatch');
});
}

const emptyPatterns = [
'',
`
`,
' '
];
for (const pat of emptyPatterns) {
test(`no pattern ("${pat}")`, () => {
const regex = verboseRegExp(pat);

expect(regex.source).to.equal('(?:)', 'mismatch');
});
}
});
Loading