From 407033976b688bec67699ee10c97ef7d9ac01859 Mon Sep 17 00:00:00 2001 From: Kim-Adeline Miguel Date: Wed, 3 Jul 2019 09:22:59 -0700 Subject: [PATCH 01/10] Update indent regex --- news/2 Fixes/4241.md | 1 + src/client/extension.ts | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) create mode 100644 news/2 Fixes/4241.md diff --git a/news/2 Fixes/4241.md b/news/2 Fixes/4241.md new file mode 100644 index 000000000000..e038ff27822d --- /dev/null +++ b/news/2 Fixes/4241.md @@ -0,0 +1 @@ +Fix indentation after string literals containing escaped characters. diff --git a/src/client/extension.ts b/src/client/extension.ts index 3a22ca96ec65..90b63d6ccd76 100644 --- a/src/client/extension.ts +++ b/src/client/extension.ts @@ -182,7 +182,7 @@ async function activateUnsafe(context: ExtensionContext): Promise action: { indentAction: IndentAction.Indent } }, { - beforeText: /^(?!\s+\\)[^#\n]+\\\s*/, + beforeText: /^(?!\s+\\)[^#\n]+\\$/, action: { indentAction: IndentAction.Indent } }, { From ca8b3ec44ee4364a09c851ebcd16ec22238626d3 Mon Sep 17 00:00:00 2001 From: Kim-Adeline Miguel Date: Wed, 3 Jul 2019 09:23:11 -0700 Subject: [PATCH 02/10] Prettier formatting --- src/client/extension.ts | 42 ++++++++++++++++++++--------------------- 1 file changed, 20 insertions(+), 22 deletions(-) diff --git a/src/client/extension.ts b/src/client/extension.ts index 90b63d6ccd76..5b708591a404 100644 --- a/src/client/extension.ts +++ b/src/client/extension.ts @@ -75,13 +75,7 @@ import { IDebugConfigurationService, IDebuggerBanner } from './debugger/extensio import { registerTypes as formattersRegisterTypes } from './formatters/serviceRegistry'; import { AutoSelectionRule, IInterpreterAutoSelectionRule, IInterpreterAutoSelectionService } from './interpreter/autoSelection/types'; import { IInterpreterSelector } from './interpreter/configuration/types'; -import { - ICondaService, - IInterpreterLocatorProgressService, - IInterpreterService, - InterpreterLocatorProgressHandler, - PythonInterpreter -} from './interpreter/contracts'; +import { ICondaService, IInterpreterLocatorProgressService, IInterpreterService, InterpreterLocatorProgressHandler, PythonInterpreter } from './interpreter/contracts'; import { registerTypes as interpretersRegisterTypes } from './interpreter/serviceRegistry'; import { ServiceContainer } from './ioc/container'; import { ServiceManager } from './ioc/serviceManager'; @@ -117,7 +111,7 @@ export async function activate(context: ExtensionContext): Promise const workspaceService = serviceContainer.get(IWorkspaceService); const interpreterManager = serviceContainer.get(IInterpreterService); - interpreterManager.refresh(workspaceService.hasWorkspaceFolders ? workspaceService.workspaceFolders![0].uri : undefined) + interpreterManager + .refresh(workspaceService.hasWorkspaceFolders ? workspaceService.workspaceFolders![0].uri : undefined) .catch(ex => console.error('Python Extension: interpreterManager.refresh', ex)); const jupyterExtension = extensions.getExtension('donjayamanne.jupyter'); @@ -334,9 +329,11 @@ function isUsingGlobalInterpreterInWorkspace(currentPythonPath: string, serviceC function hasUserDefinedPythonPath(resource: Resource, serviceContainer: IServiceContainer) { const workspaceService = serviceContainer.get(IWorkspaceService); const settings = workspaceService.getConfiguration('python', resource)!.inspect('pythonPath')!; - return ((settings.workspaceFolderValue && settings.workspaceFolderValue !== 'python') || + return (settings.workspaceFolderValue && settings.workspaceFolderValue !== 'python') || (settings.workspaceValue && settings.workspaceValue !== 'python') || - (settings.globalValue && settings.globalValue !== 'python')) ? true : false; + (settings.globalValue && settings.globalValue !== 'python') + ? true + : false; } function getPreferredWorkspaceInterpreter(resource: Resource, serviceContainer: IServiceContainer) { @@ -365,7 +362,10 @@ async function getActivationTelemetryProps(serviceContainer: IServiceContainer): const mainWorkspaceUri = workspaceService.hasWorkspaceFolders ? workspaceService.workspaceFolders![0].uri : undefined; const settings = configurationService.getSettings(mainWorkspaceUri); const [condaVersion, interpreter, interpreters] = await Promise.all([ - condaLocator.getCondaVersion().then(ver => ver ? ver.raw : '').catch(() => ''), + condaLocator + .getCondaVersion() + .then(ver => (ver ? ver.raw : '')) + .catch(() => ''), interpreterService.getActiveInterpreter().catch(() => undefined), interpreterService.getInterpreters(mainWorkspaceUri).catch(() => []) ]); @@ -375,10 +375,10 @@ async function getActivationTelemetryProps(serviceContainer: IServiceContainer): const usingUserDefinedInterpreter = hasUserDefinedPythonPath(mainWorkspaceUri, serviceContainer); const preferredWorkspaceInterpreter = getPreferredWorkspaceInterpreter(mainWorkspaceUri, serviceContainer); const usingGlobalInterpreter = isUsingGlobalInterpreterInWorkspace(settings.pythonPath, serviceContainer); - const usingAutoSelectedWorkspaceInterpreter = preferredWorkspaceInterpreter ? settings.pythonPath === getPreferredWorkspaceInterpreter(mainWorkspaceUri, serviceContainer) : false; - const hasPython3 = interpreters - .filter(item => item && item.version ? item.version.major === 3 : false) - .length > 0; + const usingAutoSelectedWorkspaceInterpreter = preferredWorkspaceInterpreter + ? settings.pythonPath === getPreferredWorkspaceInterpreter(mainWorkspaceUri, serviceContainer) + : false; + const hasPython3 = interpreters.filter(item => (item && item.version ? item.version.major === 3 : false)).length > 0; return { condaVersion, @@ -399,8 +399,7 @@ async function getActivationTelemetryProps(serviceContainer: IServiceContainer): function handleError(ex: Error) { notifyUser('Extension activation failed, run the \'Developer: Toggle Developer Tools\' command for more information.'); traceError('extension activation failed', ex); - sendErrorTelemetry(ex) - .ignoreErrors(); + sendErrorTelemetry(ex).ignoreErrors(); } interface IAppShell { @@ -410,13 +409,12 @@ interface IAppShell { function notifyUser(msg: string) { try { // tslint:disable-next-line:no-any - let appShell: IAppShell = (window as any as IAppShell); + let appShell: IAppShell = (window as any) as IAppShell; if (activatedServiceContainer) { // tslint:disable-next-line:no-any - appShell = activatedServiceContainer.get(IApplicationShell) as any as IAppShell; + appShell = (activatedServiceContainer.get(IApplicationShell) as any) as IAppShell; } - appShell.showErrorMessage(msg) - .ignoreErrors(); + appShell.showErrorMessage(msg).ignoreErrors(); } catch (ex) { // ignore } From 40329bda4b66c75602ec6e6b513a9285f8ca8fb1 Mon Sep 17 00:00:00 2001 From: Kim-Adeline Miguel Date: Thu, 4 Jul 2019 11:10:37 -0700 Subject: [PATCH 03/10] Add regex tests --- src/client/extension.ts | 4 +++- src/test/extension.unit.test.ts | 20 ++++++++++++++++++++ 2 files changed, 23 insertions(+), 1 deletion(-) diff --git a/src/client/extension.ts b/src/client/extension.ts index 5b708591a404..a3fbf47c04f1 100644 --- a/src/client/extension.ts +++ b/src/client/extension.ts @@ -106,6 +106,8 @@ durations.codeLoadingTime = stopWatch.elapsedTime; const activationDeferred = createDeferred(); let activatedServiceContainer: ServiceContainer | undefined; +export const MULTILINE_SEPARATOR_INDENT_REGEX = /^(?!\s+\\)[^#\n]+\\$/; + export async function activate(context: ExtensionContext): Promise { try { return await activateUnsafe(context); @@ -177,7 +179,7 @@ async function activateUnsafe(context: ExtensionContext): Promise action: { indentAction: IndentAction.Indent } }, { - beforeText: /^(?!\s+\\)[^#\n]+\\$/, + beforeText: MULTILINE_SEPARATOR_INDENT_REGEX, action: { indentAction: IndentAction.Indent } }, { diff --git a/src/test/extension.unit.test.ts b/src/test/extension.unit.test.ts index 553be3f7121e..1b38fa921387 100644 --- a/src/test/extension.unit.test.ts +++ b/src/test/extension.unit.test.ts @@ -6,11 +6,18 @@ // tslint:disable:no-any import { expect } from 'chai'; +import * as sinon from 'sinon'; import { buildApi } from '../client/api'; import { EXTENSION_ROOT_DIR } from '../client/common/constants'; const expectedPath = `${EXTENSION_ROOT_DIR.fileToCommandArgument()}/pythonFiles/ptvsd_launcher.py`; +// Stub sourceMapSupport.initialize before we import from extension.ts (we don't actually need it) +// tslint:disable-next-line: no-require-imports no-var-requires +const sourceMapSupport = require('../client/sourceMapSupport'); +sinon.stub(sourceMapSupport, 'initialize'); +import { MULTILINE_SEPARATOR_INDENT_REGEX } from '../client/extension'; + suite('Extension API Debugger', () => { test('Test debug launcher args (no-wait)', async () => { const args = await buildApi(Promise.resolve()).debug.getRemoteLauncherCommand('something', 1234, false); @@ -22,4 +29,17 @@ suite('Extension API Debugger', () => { const expectedArgs = [expectedPath, '--default', '--host', 'something', '--port', '1234', '--wait']; expect(args).to.be.deep.equal(expectedArgs); }); + test('Multiline separator indent regex should not pick up strings with no multiline separator', async () => { + const matches = 'test string'.match(MULTILINE_SEPARATOR_INDENT_REGEX); + expect(matches).to.be.equal(null, 'Multiline separator indent regex for regular strings should not have matches'); + }); + test('Multiline separator indent regex should not pick up strings with escaped characters', async () => { + const matches = '\t test \n'.match(MULTILINE_SEPARATOR_INDENT_REGEX); + expect(matches).to.be.equal(null, 'Multiline separator indent regex for strings with escaped characters should not have matches'); + }); + test('Multiline separator indent regex should pick up strings ending with a multiline separator', async () => { + const matches = 'test \\'.match(MULTILINE_SEPARATOR_INDENT_REGEX); + expect(matches).to.not.be.equal(null, 'Multiline separator indent regex for strings with newline separator should have matches'); + expect(matches!.length).to.equal(1); + }); }); From f34ca57806e8c9c4d27fc92c0cc7a09c8d407017 Mon Sep 17 00:00:00 2001 From: Kim-Adeline Miguel Date: Thu, 4 Jul 2019 11:14:29 -0700 Subject: [PATCH 04/10] Undo prettier changes --- src/client/extension.ts | 32 +++++++++++++++++--------------- 1 file changed, 17 insertions(+), 15 deletions(-) diff --git a/src/client/extension.ts b/src/client/extension.ts index a3fbf47c04f1..85210b44201c 100644 --- a/src/client/extension.ts +++ b/src/client/extension.ts @@ -75,7 +75,13 @@ import { IDebugConfigurationService, IDebuggerBanner } from './debugger/extensio import { registerTypes as formattersRegisterTypes } from './formatters/serviceRegistry'; import { AutoSelectionRule, IInterpreterAutoSelectionRule, IInterpreterAutoSelectionService } from './interpreter/autoSelection/types'; import { IInterpreterSelector } from './interpreter/configuration/types'; -import { ICondaService, IInterpreterLocatorProgressService, IInterpreterService, InterpreterLocatorProgressHandler, PythonInterpreter } from './interpreter/contracts'; +import { + ICondaService, + IInterpreterLocatorProgressService, + IInterpreterService, + InterpreterLocatorProgressHandler, + PythonInterpreter +} from './interpreter/contracts'; import { registerTypes as interpretersRegisterTypes } from './interpreter/serviceRegistry'; import { ServiceContainer } from './ioc/container'; import { ServiceManager } from './ioc/serviceManager'; @@ -150,8 +156,7 @@ async function activateUnsafe(context: ExtensionContext): Promise const workspaceService = serviceContainer.get(IWorkspaceService); const interpreterManager = serviceContainer.get(IInterpreterService); - interpreterManager - .refresh(workspaceService.hasWorkspaceFolders ? workspaceService.workspaceFolders![0].uri : undefined) + interpreterManager.refresh(workspaceService.hasWorkspaceFolders ? workspaceService.workspaceFolders![0].uri : undefined) .catch(ex => console.error('Python Extension: interpreterManager.refresh', ex)); const jupyterExtension = extensions.getExtension('donjayamanne.jupyter'); @@ -331,11 +336,9 @@ function isUsingGlobalInterpreterInWorkspace(currentPythonPath: string, serviceC function hasUserDefinedPythonPath(resource: Resource, serviceContainer: IServiceContainer) { const workspaceService = serviceContainer.get(IWorkspaceService); const settings = workspaceService.getConfiguration('python', resource)!.inspect('pythonPath')!; - return (settings.workspaceFolderValue && settings.workspaceFolderValue !== 'python') || + return ((settings.workspaceFolderValue && settings.workspaceFolderValue !== 'python') || (settings.workspaceValue && settings.workspaceValue !== 'python') || - (settings.globalValue && settings.globalValue !== 'python') - ? true - : false; + (settings.globalValue && settings.globalValue !== 'python')) ? true : false; } function getPreferredWorkspaceInterpreter(resource: Resource, serviceContainer: IServiceContainer) { @@ -364,10 +367,7 @@ async function getActivationTelemetryProps(serviceContainer: IServiceContainer): const mainWorkspaceUri = workspaceService.hasWorkspaceFolders ? workspaceService.workspaceFolders![0].uri : undefined; const settings = configurationService.getSettings(mainWorkspaceUri); const [condaVersion, interpreter, interpreters] = await Promise.all([ - condaLocator - .getCondaVersion() - .then(ver => (ver ? ver.raw : '')) - .catch(() => ''), + condaLocator.getCondaVersion().then(ver => ver ? ver.raw : '').catch(() => ''), interpreterService.getActiveInterpreter().catch(() => undefined), interpreterService.getInterpreters(mainWorkspaceUri).catch(() => []) ]); @@ -401,7 +401,8 @@ async function getActivationTelemetryProps(serviceContainer: IServiceContainer): function handleError(ex: Error) { notifyUser('Extension activation failed, run the \'Developer: Toggle Developer Tools\' command for more information.'); traceError('extension activation failed', ex); - sendErrorTelemetry(ex).ignoreErrors(); + sendErrorTelemetry(ex) + .ignoreErrors(); } interface IAppShell { @@ -411,12 +412,13 @@ interface IAppShell { function notifyUser(msg: string) { try { // tslint:disable-next-line:no-any - let appShell: IAppShell = (window as any) as IAppShell; + let appShell: IAppShell = (window as any as IAppShell); if (activatedServiceContainer) { // tslint:disable-next-line:no-any - appShell = (activatedServiceContainer.get(IApplicationShell) as any) as IAppShell; + appShell = activatedServiceContainer.get(IApplicationShell) as any as IAppShell; } - appShell.showErrorMessage(msg).ignoreErrors(); + appShell.showErrorMessage(msg) + .ignoreErrors(); } catch (ex) { // ignore } From 863bd0f9b3d7a25c5d007375850c3f63f3f5d5b2 Mon Sep 17 00:00:00 2001 From: Kim-Adeline Miguel Date: Thu, 4 Jul 2019 11:17:06 -0700 Subject: [PATCH 05/10] Forgot some --- src/client/extension.ts | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/client/extension.ts b/src/client/extension.ts index 85210b44201c..7d099312836b 100644 --- a/src/client/extension.ts +++ b/src/client/extension.ts @@ -119,7 +119,7 @@ export async function activate(context: ExtensionContext): Promise (item && item.version ? item.version.major === 3 : false)).length > 0; + const usingAutoSelectedWorkspaceInterpreter = preferredWorkspaceInterpreter ? settings.pythonPath === getPreferredWorkspaceInterpreter(mainWorkspaceUri, serviceContainer) : false; + const hasPython3 = interpreters + .filter(item => (item && item.version ? item.version.major === 3 : false)) + .length > 0; return { condaVersion, From d0c229622eb0fc63d2c8165d195467a09f2bb02d Mon Sep 17 00:00:00 2001 From: Kim-Adeline Miguel Date: Thu, 4 Jul 2019 11:17:42 -0700 Subject: [PATCH 06/10] One more --- src/client/extension.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/client/extension.ts b/src/client/extension.ts index 7d099312836b..7c0b865dd3cc 100644 --- a/src/client/extension.ts +++ b/src/client/extension.ts @@ -379,7 +379,7 @@ async function getActivationTelemetryProps(serviceContainer: IServiceContainer): const usingGlobalInterpreter = isUsingGlobalInterpreterInWorkspace(settings.pythonPath, serviceContainer); const usingAutoSelectedWorkspaceInterpreter = preferredWorkspaceInterpreter ? settings.pythonPath === getPreferredWorkspaceInterpreter(mainWorkspaceUri, serviceContainer) : false; const hasPython3 = interpreters - .filter(item => (item && item.version ? item.version.major === 3 : false)) + .filter(item => item && item.version ? item.version.major === 3 : false) .length > 0; return { From ea32ce2486e48d585fdf512e0aba62be2593fc0b Mon Sep 17 00:00:00 2001 From: Kim-Adeline Miguel Date: Thu, 4 Jul 2019 12:13:22 -0700 Subject: [PATCH 07/10] Simplify & fix tests --- src/test/extension.unit.test.ts | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/src/test/extension.unit.test.ts b/src/test/extension.unit.test.ts index 1b38fa921387..299758363e3d 100644 --- a/src/test/extension.unit.test.ts +++ b/src/test/extension.unit.test.ts @@ -30,16 +30,15 @@ suite('Extension API Debugger', () => { expect(args).to.be.deep.equal(expectedArgs); }); test('Multiline separator indent regex should not pick up strings with no multiline separator', async () => { - const matches = 'test string'.match(MULTILINE_SEPARATOR_INDENT_REGEX); - expect(matches).to.be.equal(null, 'Multiline separator indent regex for regular strings should not have matches'); + const result = MULTILINE_SEPARATOR_INDENT_REGEX.test('a = "test"'); + expect (result).to.be.equal(false, 'Multiline separator indent regex for regular strings should not have matches'); }); test('Multiline separator indent regex should not pick up strings with escaped characters', async () => { - const matches = '\t test \n'.match(MULTILINE_SEPARATOR_INDENT_REGEX); - expect(matches).to.be.equal(null, 'Multiline separator indent regex for strings with escaped characters should not have matches'); + const result = MULTILINE_SEPARATOR_INDENT_REGEX.test('a = \'hello \\n\''); + expect (result).to.be.equal(false, 'Multiline separator indent regex for strings with escaped characters should not have matches'); }); test('Multiline separator indent regex should pick up strings ending with a multiline separator', async () => { - const matches = 'test \\'.match(MULTILINE_SEPARATOR_INDENT_REGEX); - expect(matches).to.not.be.equal(null, 'Multiline separator indent regex for strings with newline separator should have matches'); - expect(matches!.length).to.equal(1); + const result = MULTILINE_SEPARATOR_INDENT_REGEX.test('a = \'multiline \\'); + expect (result).to.be.equal(true, 'Multiline separator indent regex for strings with newline separator should have matches'); }); }); From f5daf78c2ac8dd2c53754dc557aff0370b9df840 Mon Sep 17 00:00:00 2001 From: Kim-Adeline Miguel Date: Thu, 4 Jul 2019 14:20:14 -0700 Subject: [PATCH 08/10] More descriptive comment --- src/test/extension.unit.test.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/test/extension.unit.test.ts b/src/test/extension.unit.test.ts index 299758363e3d..a69e571ad5a2 100644 --- a/src/test/extension.unit.test.ts +++ b/src/test/extension.unit.test.ts @@ -12,7 +12,8 @@ import { EXTENSION_ROOT_DIR } from '../client/common/constants'; const expectedPath = `${EXTENSION_ROOT_DIR.fileToCommandArgument()}/pythonFiles/ptvsd_launcher.py`; -// Stub sourceMapSupport.initialize before we import from extension.ts (we don't actually need it) +// Stub sourceMapSupport.initialize before we import from extension.ts +// (it's called at the top of the file but we don't need it here) // tslint:disable-next-line: no-require-imports no-var-requires const sourceMapSupport = require('../client/sourceMapSupport'); sinon.stub(sourceMapSupport, 'initialize'); From b62a109a566a2e87e6b6f73de623d3c4dacbc8f0 Mon Sep 17 00:00:00 2001 From: Kim-Adeline Miguel Date: Mon, 8 Jul 2019 13:51:03 -0700 Subject: [PATCH 09/10] Move language config to another file --- src/client/extension.ts | 31 ++-------------- src/client/language/languageConfiguration.ts | 35 +++++++++++++++++++ src/test/extension.unit.test.ts | 20 ----------- .../languageConfiguration.unit.test.ts | 23 ++++++++++++ 4 files changed, 61 insertions(+), 48 deletions(-) create mode 100644 src/client/language/languageConfiguration.ts create mode 100644 src/test/language/languageConfiguration.unit.test.ts diff --git a/src/client/extension.ts b/src/client/extension.ts index 7c0b865dd3cc..a017a0ffb0b3 100644 --- a/src/client/extension.ts +++ b/src/client/extension.ts @@ -25,7 +25,6 @@ import { Disposable, ExtensionContext, extensions, - IndentAction, languages, Memento, OutputChannel, @@ -41,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, PYTHON_LANGUAGE, STANDARD_OUTPUT_CHANNEL } from './common/constants'; +import { Commands, isTestExecution, PYTHON, 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'; @@ -86,6 +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 { LinterCommands } from './linters/linterCommands'; import { registerTypes as lintersRegisterTypes } from './linters/serviceRegistry'; import { ILintingEngine } from './linters/types'; @@ -112,8 +112,6 @@ durations.codeLoadingTime = stopWatch.elapsedTime; const activationDeferred = createDeferred(); let activatedServiceContainer: ServiceContainer | undefined; -export const MULTILINE_SEPARATOR_INDENT_REGEX = /^(?!\s+\\)[^#\n]+\\$/; - export async function activate(context: ExtensionContext): Promise { try { return await activateUnsafe(context); @@ -175,30 +173,7 @@ async function activateUnsafe(context: ExtensionContext): Promise const linterProvider = new LinterProvider(context, serviceManager); context.subscriptions.push(linterProvider); - // Enable indentAction - // tslint:disable-next-line:no-non-null-assertion - languages.setLanguageConfiguration(PYTHON_LANGUAGE, { - onEnterRules: [ - { - 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: /^\s*#.*/, - afterText: /.+$/, - action: { indentAction: IndentAction.None, appendText: '# ' } - }, - { - beforeText: /^\s+(continue|break|return)\b.*/, - afterText: /\s+$/, - action: { indentAction: IndentAction.Outdent } - } - ] - }); + setLanguageConfiguration(); if (pythonSettings && pythonSettings.formatting && pythonSettings.formatting.provider !== 'internalConsole') { const formatProvider = new PythonFormattingEditProvider(context, serviceContainer); diff --git a/src/client/language/languageConfiguration.ts b/src/client/language/languageConfiguration.ts new file mode 100644 index 000000000000..7a0ba92e86eb --- /dev/null +++ b/src/client/language/languageConfiguration.ts @@ -0,0 +1,35 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. +'use strict'; + +import { IndentAction, languages } from 'vscode'; +import { PYTHON_LANGUAGE } from '../common/constants'; + +export const MULTILINE_SEPARATOR_INDENT_REGEX = /^(?!\s+\\)[^#\n]+\\$/; + +export function setLanguageConfiguration() { + // Enable indentAction + // tslint:disable-next-line:no-non-null-assertion + languages.setLanguageConfiguration(PYTHON_LANGUAGE, { + onEnterRules: [ + { + 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: /^\s*#.*/, + afterText: /.+$/, + action: { indentAction: IndentAction.None, appendText: '# ' } + }, + { + beforeText: /^\s+(continue|break|return)\b.*/, + afterText: /\s+$/, + action: { indentAction: IndentAction.Outdent } + } + ] + }); +} diff --git a/src/test/extension.unit.test.ts b/src/test/extension.unit.test.ts index a69e571ad5a2..553be3f7121e 100644 --- a/src/test/extension.unit.test.ts +++ b/src/test/extension.unit.test.ts @@ -6,19 +6,11 @@ // tslint:disable:no-any import { expect } from 'chai'; -import * as sinon from 'sinon'; import { buildApi } from '../client/api'; import { EXTENSION_ROOT_DIR } from '../client/common/constants'; const expectedPath = `${EXTENSION_ROOT_DIR.fileToCommandArgument()}/pythonFiles/ptvsd_launcher.py`; -// Stub sourceMapSupport.initialize before we import from extension.ts -// (it's called at the top of the file but we don't need it here) -// tslint:disable-next-line: no-require-imports no-var-requires -const sourceMapSupport = require('../client/sourceMapSupport'); -sinon.stub(sourceMapSupport, 'initialize'); -import { MULTILINE_SEPARATOR_INDENT_REGEX } from '../client/extension'; - suite('Extension API Debugger', () => { test('Test debug launcher args (no-wait)', async () => { const args = await buildApi(Promise.resolve()).debug.getRemoteLauncherCommand('something', 1234, false); @@ -30,16 +22,4 @@ suite('Extension API Debugger', () => { const expectedArgs = [expectedPath, '--default', '--host', 'something', '--port', '1234', '--wait']; expect(args).to.be.deep.equal(expectedArgs); }); - test('Multiline separator indent regex should not pick up strings with no multiline separator', async () => { - const result = MULTILINE_SEPARATOR_INDENT_REGEX.test('a = "test"'); - expect (result).to.be.equal(false, 'Multiline separator indent regex for regular strings should not have matches'); - }); - test('Multiline separator indent regex should not pick up strings with escaped characters', async () => { - const result = MULTILINE_SEPARATOR_INDENT_REGEX.test('a = \'hello \\n\''); - expect (result).to.be.equal(false, 'Multiline separator indent regex for strings with escaped characters should not have matches'); - }); - test('Multiline separator indent regex should pick up strings ending with a multiline separator', async () => { - const result = MULTILINE_SEPARATOR_INDENT_REGEX.test('a = \'multiline \\'); - expect (result).to.be.equal(true, 'Multiline separator indent regex for strings with newline separator should have matches'); - }); }); diff --git a/src/test/language/languageConfiguration.unit.test.ts b/src/test/language/languageConfiguration.unit.test.ts new file mode 100644 index 000000000000..1356995cfdf4 --- /dev/null +++ b/src/test/language/languageConfiguration.unit.test.ts @@ -0,0 +1,23 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +'use strict'; + +import { expect } from 'chai'; + +import { MULTILINE_SEPARATOR_INDENT_REGEX } from '../../client/language/languageConfiguration'; + +suite('Language configuration regexes', () => { + test('Multiline separator indent regex should not pick up strings with no multiline separator', async () => { + const result = MULTILINE_SEPARATOR_INDENT_REGEX.test('a = "test"'); + expect (result).to.be.equal(false, 'Multiline separator indent regex for regular strings should not have matches'); + }); + test('Multiline separator indent regex should not pick up strings with escaped characters', async () => { + const result = MULTILINE_SEPARATOR_INDENT_REGEX.test('a = \'hello \\n\''); + expect (result).to.be.equal(false, 'Multiline separator indent regex for strings with escaped characters should not have matches'); + }); + test('Multiline separator indent regex should pick up strings ending with a multiline separator', async () => { + const result = MULTILINE_SEPARATOR_INDENT_REGEX.test('a = \'multiline \\'); + expect (result).to.be.equal(true, 'Multiline separator indent regex for strings with newline separator should have matches'); + }); +}); From 0a80f0c8d15229033657d0935393bf301a69284e Mon Sep 17 00:00:00 2001 From: Kim-Adeline Miguel Date: Mon, 8 Jul 2019 13:53:16 -0700 Subject: [PATCH 10/10] Remove uneeded tslint disable rule --- src/client/language/languageConfiguration.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/src/client/language/languageConfiguration.ts b/src/client/language/languageConfiguration.ts index 7a0ba92e86eb..19eebd8d0131 100644 --- a/src/client/language/languageConfiguration.ts +++ b/src/client/language/languageConfiguration.ts @@ -9,7 +9,6 @@ export const MULTILINE_SEPARATOR_INDENT_REGEX = /^(?!\s+\\)[^#\n]+\\$/; export function setLanguageConfiguration() { // Enable indentAction - // tslint:disable-next-line:no-non-null-assertion languages.setLanguageConfiguration(PYTHON_LANGUAGE, { onEnterRules: [ {