From 577b3d059f73728fe1ccc2b58bddd97632033ee4 Mon Sep 17 00:00:00 2001 From: luabud Date: Mon, 14 Feb 2022 19:25:46 -0800 Subject: [PATCH 01/18] Create new Python file command --- package.json | 13 ++++++++++++ src/client/common/application/commands.ts | 1 + .../application/commands/createFileCommand.ts | 21 +++++++++++++++++++ src/client/common/constants.ts | 1 + src/client/common/serviceRegistry.ts | 5 +++++ 5 files changed, 41 insertions(+) create mode 100644 src/client/common/application/commands/createFileCommand.ts diff --git a/package.json b/package.json index fea002930776..074f703213ae 100644 --- a/package.json +++ b/package.json @@ -65,6 +65,7 @@ "onDebugInitialConfigurations", "onLanguage:python", "onDebugResolve:python", + "onCommand:python.createNewFile", "onCommand:python.execInTerminal", "onCommand:python.debugInTerminal", "onCommand:python.sortImports", @@ -322,6 +323,11 @@ } ], "commands": [ + { + "title": "New Python File", + "category": "Python", + "command": "python.createNewFile" + }, { "category": "Python", "command": "python.analysis.restartLanguageServer", @@ -1863,6 +1869,13 @@ "when": "resourceLangId == python && !virtualWorkspace && shellExecutionSupported" } ], + "file/newFile": [ + { + "title": "New Python File", + "command": "python.createNewFile", + "category": "file" + } + ], "view/title": [ { "command": "python.refreshTests", diff --git a/src/client/common/application/commands.ts b/src/client/common/application/commands.ts index eb4e827d42bb..aaf1a710a947 100644 --- a/src/client/common/application/commands.ts +++ b/src/client/common/application/commands.ts @@ -42,6 +42,7 @@ interface ICommandNameWithoutArgumentTypeMapping { [Commands.PickLocalProcess]: []; [Commands.ClearStorage]: []; [Commands.ReportIssue]: []; + [Commands.CreateNewFile]: []; [Commands.RefreshTensorBoard]: []; [LSCommands.RestartLS]: []; } diff --git a/src/client/common/application/commands/createFileCommand.ts b/src/client/common/application/commands/createFileCommand.ts new file mode 100644 index 000000000000..5c71bf8e0d32 --- /dev/null +++ b/src/client/common/application/commands/createFileCommand.ts @@ -0,0 +1,21 @@ +import { injectable, inject } from 'inversify'; +import { IExtensionSingleActivationService } from '../../../activation/types'; +import { Commands } from '../../constants'; +import { ICommandManager } from '../types'; +import * as vscode from 'vscode'; + +@injectable() +export class CreatePythonFileCommandHandler implements IExtensionSingleActivationService { + public readonly supportedWorkspaceTypes = { untrustedWorkspace: true, virtualWorkspace: true }; + + constructor(@inject(ICommandManager) private readonly commandManager: ICommandManager) {} + + public async activate(): Promise { + this.commandManager.registerCommand(Commands.CreateNewFile, this.createPythonFile, this); + } + + public async createPythonFile(): Promise { + const newFile = await vscode.workspace.openTextDocument({ language: 'python' }); + vscode.window.showTextDocument(newFile); + } +} diff --git a/src/client/common/constants.ts b/src/client/common/constants.ts index 6cd283daab99..2fa39e0219b6 100644 --- a/src/client/common/constants.ts +++ b/src/client/common/constants.ts @@ -49,6 +49,7 @@ export namespace Commands { export const ViewOutput = 'python.viewOutput'; export const Start_REPL = 'python.startREPL'; export const Create_Terminal = 'python.createTerminal'; + export const CreateNewFile = 'python.createNewFile'; export const Set_Linter = 'python.setLinter'; export const Enable_Linter = 'python.enableLinting'; export const Run_Linter = 'python.runLinting'; diff --git a/src/client/common/serviceRegistry.ts b/src/client/common/serviceRegistry.ts index 674597928db0..d78642a56d28 100644 --- a/src/client/common/serviceRegistry.ts +++ b/src/client/common/serviceRegistry.ts @@ -31,6 +31,7 @@ import { ClipboardService } from './application/clipboard'; import { CommandManager } from './application/commandManager'; import { ReloadVSCodeCommandHandler } from './application/commands/reloadCommand'; import { ReportIssueCommandHandler } from './application/commands/reportIssueCommand'; +import { CreatePythonFileCommandHandler } from './application/commands/createFileCommand'; import { DebugService } from './application/debugService'; import { DebugSessionTelemetry } from './application/debugSessionTelemetry'; import { DocumentManager } from './application/documentManager'; @@ -198,6 +199,10 @@ export function registerTypes(serviceManager: IServiceManager): void { IExtensionSingleActivationService, ReportIssueCommandHandler, ); + serviceManager.addSingleton( + IExtensionSingleActivationService, + CreatePythonFileCommandHandler, + ); serviceManager.addSingleton(IExtensionChannelService, ExtensionChannelService); serviceManager.addSingleton( IExtensionChannelRule, From 1408619e081cbfec8c13259ff0ac5582c5093e64 Mon Sep 17 00:00:00 2001 From: luabud Date: Tue, 15 Feb 2022 09:10:18 -0800 Subject: [PATCH 02/18] Rename command --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 074f703213ae..ff93098d4ed6 100644 --- a/package.json +++ b/package.json @@ -1871,7 +1871,7 @@ ], "file/newFile": [ { - "title": "New Python File", + "title": "Python File", "command": "python.createNewFile", "category": "file" } From 87d9174665b267690046267bb8753d68ed45605f Mon Sep 17 00:00:00 2001 From: luabud Date: Tue, 15 Feb 2022 09:20:17 -0800 Subject: [PATCH 03/18] Format documents --- package.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/package.json b/package.json index ff93098d4ed6..a9e1a0313c2f 100644 --- a/package.json +++ b/package.json @@ -324,10 +324,10 @@ ], "commands": [ { - "title": "New Python File", + "title": "New Python File", "category": "Python", - "command": "python.createNewFile" - }, + "command": "python.createNewFile" + }, { "category": "Python", "command": "python.analysis.restartLanguageServer", From da11fef5ef6a3a02f02423cc5df0e9035a0020bc Mon Sep 17 00:00:00 2001 From: luabud Date: Tue, 15 Feb 2022 09:22:12 -0800 Subject: [PATCH 04/18] Add a news entry --- news/1 Enhancements/18376.md | 1 + 1 file changed, 1 insertion(+) create mode 100644 news/1 Enhancements/18376.md diff --git a/news/1 Enhancements/18376.md b/news/1 Enhancements/18376.md new file mode 100644 index 000000000000..394342d70349 --- /dev/null +++ b/news/1 Enhancements/18376.md @@ -0,0 +1 @@ +Implement a "New Python File" command \ No newline at end of file From eff117255da6ccc5125d5a01cbf733d15e0179d8 Mon Sep 17 00:00:00 2001 From: luabud Date: Tue, 15 Feb 2022 18:11:04 -0800 Subject: [PATCH 05/18] Add telemetry and add experimental setting --- package.json | 13 +++++++++++-- .../application/commands/createFileCommand.ts | 6 ++++++ src/client/telemetry/constants.ts | 1 + src/client/telemetry/index.ts | 7 +++++++ 4 files changed, 25 insertions(+), 2 deletions(-) diff --git a/package.json b/package.json index a9e1a0313c2f..c199c0620f4b 100644 --- a/package.json +++ b/package.json @@ -326,7 +326,8 @@ { "title": "New Python File", "category": "Python", - "command": "python.createNewFile" + "command": "python.createNewFile", + "when": "config.python.createNewFileEnabled" }, { "category": "Python", @@ -509,6 +510,13 @@ "scope": "machine", "type": "string" }, + "python.createNewFileEnabled": { + "default": "false", + "description": "Enable the Python: New Python File command.", + "scope": "machine", + "type": "boolean", + "tags": ["experimental"] + }, "python.defaultInterpreterPath": { "default": "python", "description": "Path to default Python to use when extension loads up for the first time, no longer used once an interpreter is selected for the workspace. See https://aka.ms/AAfekmf to understand when this is used.", @@ -1873,7 +1881,8 @@ { "title": "Python File", "command": "python.createNewFile", - "category": "file" + "category": "file", + "when": "config.python.createNewFileEnabled" } ], "view/title": [ diff --git a/src/client/common/application/commands/createFileCommand.ts b/src/client/common/application/commands/createFileCommand.ts index 5c71bf8e0d32..e4a9f87ae044 100644 --- a/src/client/common/application/commands/createFileCommand.ts +++ b/src/client/common/application/commands/createFileCommand.ts @@ -3,6 +3,8 @@ import { IExtensionSingleActivationService } from '../../../activation/types'; import { Commands } from '../../constants'; import { ICommandManager } from '../types'; import * as vscode from 'vscode'; +import { sendTelemetryEvent } from '../../../telemetry'; +import { EventName } from '../../../telemetry/constants'; @injectable() export class CreatePythonFileCommandHandler implements IExtensionSingleActivationService { @@ -11,11 +13,15 @@ export class CreatePythonFileCommandHandler implements IExtensionSingleActivatio constructor(@inject(ICommandManager) private readonly commandManager: ICommandManager) {} public async activate(): Promise { + if (!vscode.workspace.getConfiguration('python').get('createNewFileEnabled')) { + return; + } this.commandManager.registerCommand(Commands.CreateNewFile, this.createPythonFile, this); } public async createPythonFile(): Promise { const newFile = await vscode.workspace.openTextDocument({ language: 'python' }); vscode.window.showTextDocument(newFile); + sendTelemetryEvent(EventName.CREATE_NEW_FILE_COMMAND); } } diff --git a/src/client/telemetry/constants.ts b/src/client/telemetry/constants.ts index 18979b117c8a..0845613f45f9 100644 --- a/src/client/telemetry/constants.ts +++ b/src/client/telemetry/constants.ts @@ -94,6 +94,7 @@ export enum EventName { SELECT_LINTER = 'LINTING.SELECT', USE_REPORT_ISSUE_COMMAND = 'USE_REPORT_ISSUE_COMMAND', + CREATE_NEW_FILE_COMMAND = 'CREATE_NEW_FILE_COMMAND', LINTER_NOT_INSTALLED_PROMPT = 'LINTER_NOT_INSTALLED_PROMPT', HASHED_PACKAGE_NAME = 'HASHED_PACKAGE_NAME', diff --git a/src/client/telemetry/index.ts b/src/client/telemetry/index.ts index 21b615d19434..9d0a0bf36295 100644 --- a/src/client/telemetry/index.ts +++ b/src/client/telemetry/index.ts @@ -1592,6 +1592,13 @@ export interface IEventNamePropertyMapping { "use_report_issue_command" : { } */ [EventName.USE_REPORT_ISSUE_COMMAND]: unknown; + /** + * Telemetry event sent when the New Python File command is executed. + */ + /* __GDPR__ + "create_new_file_command" : { } + */ + [EventName.CREATE_NEW_FILE_COMMAND]: unknown; /** * Telemetry event sent once on session start with details on which experiments are opted into and opted out from. */ From 498441629162e81d275ff86a4737562e987cfa56 Mon Sep 17 00:00:00 2001 From: luabud Date: Tue, 15 Feb 2022 18:32:33 -0800 Subject: [PATCH 06/18] Add tests --- .../createNewFileCommand.unit.test.ts | 23 +++++++++++++++++++ 1 file changed, 23 insertions(+) create mode 100644 src/test/common/application/commands/createNewFileCommand.unit.test.ts diff --git a/src/test/common/application/commands/createNewFileCommand.unit.test.ts b/src/test/common/application/commands/createNewFileCommand.unit.test.ts new file mode 100644 index 000000000000..7352290ba9b9 --- /dev/null +++ b/src/test/common/application/commands/createNewFileCommand.unit.test.ts @@ -0,0 +1,23 @@ +import { anything, instance, mock, verify } from 'ts-mockito'; +import { Commands } from '../../../../client/common/constants'; +import { CommandManager } from '../../../../client/common/application/commandManager'; +import { CreatePythonFileCommandHandler } from '../../../../client/common/application/commands/createFileCommand'; +import { ICommandManager } from '../../../../client/common/application/types'; + +suite('Create New Python File Commmand', () => { + let createNewFileCommandHandler: CreatePythonFileCommandHandler; + let cmdManager: ICommandManager; + + setup(async () => { + cmdManager = mock(CommandManager); + createNewFileCommandHandler = new CreatePythonFileCommandHandler(instance(cmdManager)); + await createNewFileCommandHandler.activate(); + }); + + test('Create a Python file if command is executed', async () => { + await createNewFileCommandHandler.createPythonFile(); + + verify(cmdManager.registerCommand(Commands.CreateNewFile, anything())).once(); + verify(cmdManager.executeCommand('python.createNewFile')).once(); + }); +}); From 54f762161b3892caf2c5143cddcca48ad14c75ad Mon Sep 17 00:00:00 2001 From: luabud Date: Tue, 15 Feb 2022 18:33:13 -0800 Subject: [PATCH 07/18] Format document --- package.json | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/package.json b/package.json index c199c0620f4b..34295b61a07f 100644 --- a/package.json +++ b/package.json @@ -515,7 +515,9 @@ "description": "Enable the Python: New Python File command.", "scope": "machine", "type": "boolean", - "tags": ["experimental"] + "tags": [ + "experimental" + ] }, "python.defaultInterpreterPath": { "default": "python", From 0d47caa8fd05919112dff36a187c5e8a208cb808 Mon Sep 17 00:00:00 2001 From: luabud Date: Tue, 15 Feb 2022 18:42:06 -0800 Subject: [PATCH 08/18] Disable eslint class-methods-use-this --- src/client/common/application/commands/createFileCommand.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/client/common/application/commands/createFileCommand.ts b/src/client/common/application/commands/createFileCommand.ts index e4a9f87ae044..d95040c4a468 100644 --- a/src/client/common/application/commands/createFileCommand.ts +++ b/src/client/common/application/commands/createFileCommand.ts @@ -1,8 +1,8 @@ import { injectable, inject } from 'inversify'; +import * as vscode from 'vscode'; import { IExtensionSingleActivationService } from '../../../activation/types'; import { Commands } from '../../constants'; import { ICommandManager } from '../types'; -import * as vscode from 'vscode'; import { sendTelemetryEvent } from '../../../telemetry'; import { EventName } from '../../../telemetry/constants'; @@ -19,6 +19,7 @@ export class CreatePythonFileCommandHandler implements IExtensionSingleActivatio this.commandManager.registerCommand(Commands.CreateNewFile, this.createPythonFile, this); } + // eslint-disable-next-line class-methods-use-this public async createPythonFile(): Promise { const newFile = await vscode.workspace.openTextDocument({ language: 'python' }); vscode.window.showTextDocument(newFile); From a441e7784cebdbd355a3e8fbf9d056f549e50b2f Mon Sep 17 00:00:00 2001 From: luabud Date: Wed, 16 Feb 2022 09:24:27 -0800 Subject: [PATCH 09/18] Attempt to fix test --- .../commands/createNewFileCommand.unit.test.ts | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/src/test/common/application/commands/createNewFileCommand.unit.test.ts b/src/test/common/application/commands/createNewFileCommand.unit.test.ts index 7352290ba9b9..99e5c7b0fc87 100644 --- a/src/test/common/application/commands/createNewFileCommand.unit.test.ts +++ b/src/test/common/application/commands/createNewFileCommand.unit.test.ts @@ -1,16 +1,25 @@ -import { anything, instance, mock, verify } from 'ts-mockito'; +import { anything, instance, mock, verify, when } from 'ts-mockito'; import { Commands } from '../../../../client/common/constants'; import { CommandManager } from '../../../../client/common/application/commandManager'; import { CreatePythonFileCommandHandler } from '../../../../client/common/application/commands/createFileCommand'; -import { ICommandManager } from '../../../../client/common/application/types'; +import { ICommandManager, IWorkspaceService } from '../../../../client/common/application/types'; +import { MockWorkspaceConfiguration } from '../../../mocks/mockWorkspaceConfig'; suite('Create New Python File Commmand', () => { let createNewFileCommandHandler: CreatePythonFileCommandHandler; let cmdManager: ICommandManager; + let workspaceService: IWorkspaceService; setup(async () => { cmdManager = mock(CommandManager); + createNewFileCommandHandler = new CreatePythonFileCommandHandler(instance(cmdManager)); + when(cmdManager.executeCommand(anything())).thenResolve(); + when(workspaceService.getConfiguration('python')).thenReturn( + new MockWorkspaceConfiguration({ + createNewFileEnabled: true, + }), + ); await createNewFileCommandHandler.activate(); }); From 02305d88efc860aafbf9301122339440aacc6e81 Mon Sep 17 00:00:00 2001 From: luabud Date: Wed, 16 Feb 2022 11:25:50 -0800 Subject: [PATCH 10/18] Add workspace service --- .../common/application/commands/createFileCommand.ts | 9 ++++++--- .../commands/createNewFileCommand.unit.test.ts | 2 ++ 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/src/client/common/application/commands/createFileCommand.ts b/src/client/common/application/commands/createFileCommand.ts index d95040c4a468..bd50d017b5ed 100644 --- a/src/client/common/application/commands/createFileCommand.ts +++ b/src/client/common/application/commands/createFileCommand.ts @@ -2,7 +2,7 @@ import { injectable, inject } from 'inversify'; import * as vscode from 'vscode'; import { IExtensionSingleActivationService } from '../../../activation/types'; import { Commands } from '../../constants'; -import { ICommandManager } from '../types'; +import { ICommandManager, IWorkspaceService } from '../types'; import { sendTelemetryEvent } from '../../../telemetry'; import { EventName } from '../../../telemetry/constants'; @@ -10,10 +10,13 @@ import { EventName } from '../../../telemetry/constants'; export class CreatePythonFileCommandHandler implements IExtensionSingleActivationService { public readonly supportedWorkspaceTypes = { untrustedWorkspace: true, virtualWorkspace: true }; - constructor(@inject(ICommandManager) private readonly commandManager: ICommandManager) {} + constructor( + @inject(ICommandManager) private readonly commandManager: ICommandManager, + @inject(IWorkspaceService) private readonly workspaceService: IWorkspaceService, + ) {} public async activate(): Promise { - if (!vscode.workspace.getConfiguration('python').get('createNewFileEnabled')) { + if (!this.workspaceService.getConfiguration('python').get('createNewFileEnabled')) { return; } this.commandManager.registerCommand(Commands.CreateNewFile, this.createPythonFile, this); diff --git a/src/test/common/application/commands/createNewFileCommand.unit.test.ts b/src/test/common/application/commands/createNewFileCommand.unit.test.ts index 99e5c7b0fc87..501ac9165aec 100644 --- a/src/test/common/application/commands/createNewFileCommand.unit.test.ts +++ b/src/test/common/application/commands/createNewFileCommand.unit.test.ts @@ -4,6 +4,7 @@ import { CommandManager } from '../../../../client/common/application/commandMan import { CreatePythonFileCommandHandler } from '../../../../client/common/application/commands/createFileCommand'; import { ICommandManager, IWorkspaceService } from '../../../../client/common/application/types'; import { MockWorkspaceConfiguration } from '../../../mocks/mockWorkspaceConfig'; +import { WorkspaceService } from '../../../../client/common/application/workspace'; suite('Create New Python File Commmand', () => { let createNewFileCommandHandler: CreatePythonFileCommandHandler; @@ -12,6 +13,7 @@ suite('Create New Python File Commmand', () => { setup(async () => { cmdManager = mock(CommandManager); + workspaceService = mock(WorkspaceService); createNewFileCommandHandler = new CreatePythonFileCommandHandler(instance(cmdManager)); when(cmdManager.executeCommand(anything())).thenResolve(); From eb5cb17e0e9c183db693909256668f1ca71a0e09 Mon Sep 17 00:00:00 2001 From: luabud Date: Wed, 16 Feb 2022 11:29:46 -0800 Subject: [PATCH 11/18] Try to fix test again --- .../commands/createNewFileCommand.unit.test.ts | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/src/test/common/application/commands/createNewFileCommand.unit.test.ts b/src/test/common/application/commands/createNewFileCommand.unit.test.ts index 501ac9165aec..41cb8f9a31a0 100644 --- a/src/test/common/application/commands/createNewFileCommand.unit.test.ts +++ b/src/test/common/application/commands/createNewFileCommand.unit.test.ts @@ -15,7 +15,10 @@ suite('Create New Python File Commmand', () => { cmdManager = mock(CommandManager); workspaceService = mock(WorkspaceService); - createNewFileCommandHandler = new CreatePythonFileCommandHandler(instance(cmdManager)); + createNewFileCommandHandler = new CreatePythonFileCommandHandler( + instance(cmdManager), + instance(workspaceService), + ); when(cmdManager.executeCommand(anything())).thenResolve(); when(workspaceService.getConfiguration('python')).thenReturn( new MockWorkspaceConfiguration({ @@ -25,10 +28,11 @@ suite('Create New Python File Commmand', () => { await createNewFileCommandHandler.activate(); }); + test('Create Python file command is registered', async () => { + verify(cmdManager.registerCommand(Commands.CreateNewFile, anything())).once(); + }); test('Create a Python file if command is executed', async () => { await createNewFileCommandHandler.createPythonFile(); - - verify(cmdManager.registerCommand(Commands.CreateNewFile, anything())).once(); verify(cmdManager.executeCommand('python.createNewFile')).once(); }); }); From 102ac2420281a9c683f3cac835adf266ba5bcf6e Mon Sep 17 00:00:00 2001 From: luabud Date: Wed, 16 Feb 2022 14:31:27 -0800 Subject: [PATCH 12/18] Fix tests and add appShell --- .../common/application/applicationShell.ts | 11 +++++++++ .../application/commands/createFileCommand.ts | 8 +++---- src/client/common/application/types.ts | 23 +++++++++++++++++++ src/client/common/application/workspace.ts | 5 ++++ .../createNewFileCommand.unit.test.ts | 18 +++++++++++---- 5 files changed, 56 insertions(+), 9 deletions(-) diff --git a/src/client/common/application/applicationShell.ts b/src/client/common/application/applicationShell.ts index 1f243682f18c..62696c34524c 100644 --- a/src/client/common/application/applicationShell.ts +++ b/src/client/common/application/applicationShell.ts @@ -26,9 +26,12 @@ import { SaveDialogOptions, StatusBarAlignment, StatusBarItem, + TextDocument, + TextEditor, TreeView, TreeViewOptions, Uri, + ViewColumn, window, WindowState, WorkspaceFolder, @@ -100,6 +103,14 @@ export class ApplicationShell implements IApplicationShell { public showInputBox(options?: InputBoxOptions, token?: CancellationToken): Thenable { return window.showInputBox(options, token); } + public showTextDocument( + document: TextDocument, + column?: ViewColumn, + preserveFocus?: boolean, + ): Thenable { + return window.showTextDocument(document, column, preserveFocus); + } + public openUrl(url: string): void { env.openExternal(Uri.parse(url)); } diff --git a/src/client/common/application/commands/createFileCommand.ts b/src/client/common/application/commands/createFileCommand.ts index bd50d017b5ed..8929348f483d 100644 --- a/src/client/common/application/commands/createFileCommand.ts +++ b/src/client/common/application/commands/createFileCommand.ts @@ -1,8 +1,7 @@ import { injectable, inject } from 'inversify'; -import * as vscode from 'vscode'; import { IExtensionSingleActivationService } from '../../../activation/types'; import { Commands } from '../../constants'; -import { ICommandManager, IWorkspaceService } from '../types'; +import { IApplicationShell, ICommandManager, IWorkspaceService } from '../types'; import { sendTelemetryEvent } from '../../../telemetry'; import { EventName } from '../../../telemetry/constants'; @@ -13,6 +12,7 @@ export class CreatePythonFileCommandHandler implements IExtensionSingleActivatio constructor( @inject(ICommandManager) private readonly commandManager: ICommandManager, @inject(IWorkspaceService) private readonly workspaceService: IWorkspaceService, + @inject(IApplicationShell) private readonly appShell: IApplicationShell, ) {} public async activate(): Promise { @@ -24,8 +24,8 @@ export class CreatePythonFileCommandHandler implements IExtensionSingleActivatio // eslint-disable-next-line class-methods-use-this public async createPythonFile(): Promise { - const newFile = await vscode.workspace.openTextDocument({ language: 'python' }); - vscode.window.showTextDocument(newFile); + const newFile = await this.workspaceService.openTextDocument({ language: 'python' }); + this.appShell.showTextDocument(newFile); sendTelemetryEvent(EventName.CREATE_NEW_FILE_COMMAND); } } diff --git a/src/client/common/application/types.ts b/src/client/common/application/types.ts index c8335e288447..627155f82ea1 100644 --- a/src/client/common/application/types.ts +++ b/src/client/common/application/types.ts @@ -273,6 +273,19 @@ export interface IApplicationShell { */ showInputBox(options?: InputBoxOptions, token?: CancellationToken): Thenable; + /** + * Show the given document in a text editor. A {@link ViewColumn column} can be provided + * to control where the editor is being shown. Might change the {@link window.activeTextEditor active editor}. + * + * @param document A text document to be shown. + * @param column A view column in which the {@link TextEditor editor} should be shown. The default is the {@link ViewColumn.Active active}, other values + * are adjusted to be `Min(column, columnCount + 1)`, the {@link ViewColumn.Active active}-column is not adjusted. Use {@linkcode ViewColumn.Beside} + * to open the editor to the side of the currently active one. + * @param preserveFocus When `true` the editor will not take focus. + * @return A promise that resolves to an {@link TextEditor editor}. + */ + showTextDocument(document: TextDocument, column?: ViewColumn, preserveFocus?: boolean): Thenable; + /** * Creates a [QuickPick](#QuickPick) to let the user pick an item from a list * of items of type T. @@ -833,6 +846,16 @@ export interface IWorkspaceService { * @return The full configuration or a subset. */ getConfiguration(section?: string, resource?: Uri): WorkspaceConfiguration; + + /** + * Opens an untitled text document. The editor will prompt the user for a file + * path when the document is to be saved. The `options` parameter allows to + * specify the *language* and/or the *content* of the document. + * + * @param options Options to control how the document will be created. + * @return A promise that resolves to a {@link TextDocument document}. + */ + openTextDocument(options?: { language?: string; content?: string }): Thenable; } export const ITerminalManager = Symbol('ITerminalManager'); diff --git a/src/client/common/application/workspace.ts b/src/client/common/application/workspace.ts index c830401decfb..5144bfaf748b 100644 --- a/src/client/common/application/workspace.ts +++ b/src/client/common/application/workspace.ts @@ -9,6 +9,7 @@ import { Event, FileSystemWatcher, GlobPattern, + TextDocument, Uri, workspace, WorkspaceConfiguration, @@ -97,6 +98,10 @@ export class WorkspaceService implements IWorkspaceService { return workspace.onDidGrantWorkspaceTrust; } + public openTextDocument(options?: { language?: string; content?: string }): Thenable { + return workspace.openTextDocument(options); + } + private get searchExcludes() { const searchExcludes = this.getConfiguration('search.exclude'); const enabledSearchExcludes = Object.keys(searchExcludes).filter((key) => searchExcludes.get(key) === true); diff --git a/src/test/common/application/commands/createNewFileCommand.unit.test.ts b/src/test/common/application/commands/createNewFileCommand.unit.test.ts index 41cb8f9a31a0..89b7f272dbc1 100644 --- a/src/test/common/application/commands/createNewFileCommand.unit.test.ts +++ b/src/test/common/application/commands/createNewFileCommand.unit.test.ts @@ -1,38 +1,46 @@ -import { anything, instance, mock, verify, when } from 'ts-mockito'; +import { anything, deepEqual, instance, mock, verify, when } from 'ts-mockito'; +import { TextDocument } from 'vscode'; import { Commands } from '../../../../client/common/constants'; import { CommandManager } from '../../../../client/common/application/commandManager'; import { CreatePythonFileCommandHandler } from '../../../../client/common/application/commands/createFileCommand'; -import { ICommandManager, IWorkspaceService } from '../../../../client/common/application/types'; +import { IApplicationShell, ICommandManager, IWorkspaceService } from '../../../../client/common/application/types'; import { MockWorkspaceConfiguration } from '../../../mocks/mockWorkspaceConfig'; import { WorkspaceService } from '../../../../client/common/application/workspace'; +import { ApplicationShell } from '../../../../client/common/application/applicationShell'; suite('Create New Python File Commmand', () => { let createNewFileCommandHandler: CreatePythonFileCommandHandler; let cmdManager: ICommandManager; let workspaceService: IWorkspaceService; + let appShell: IApplicationShell; setup(async () => { cmdManager = mock(CommandManager); workspaceService = mock(WorkspaceService); + appShell = mock(ApplicationShell); createNewFileCommandHandler = new CreatePythonFileCommandHandler( instance(cmdManager), instance(workspaceService), + instance(appShell), ); - when(cmdManager.executeCommand(anything())).thenResolve(); when(workspaceService.getConfiguration('python')).thenReturn( new MockWorkspaceConfiguration({ createNewFileEnabled: true, }), ); + when(workspaceService.openTextDocument(deepEqual({ language: 'python' }))).thenReturn( + Promise.resolve(({} as unknown) as TextDocument), + ); await createNewFileCommandHandler.activate(); }); test('Create Python file command is registered', async () => { - verify(cmdManager.registerCommand(Commands.CreateNewFile, anything())).once(); + verify(cmdManager.registerCommand(Commands.CreateNewFile, anything(), anything())).once(); }); test('Create a Python file if command is executed', async () => { await createNewFileCommandHandler.createPythonFile(); - verify(cmdManager.executeCommand('python.createNewFile')).once(); + verify(workspaceService.openTextDocument(deepEqual({ language: 'python' }))).once(); + verify(appShell.showTextDocument(anything())).once(); }); }); From 7fc6fdb1f255ae011349d5be5a83d228189281b6 Mon Sep 17 00:00:00 2001 From: luabud Date: Wed, 16 Feb 2022 18:25:46 -0800 Subject: [PATCH 13/18] Add title for command --- .vscode/launch.json | 2 +- package.json | 2 +- package.nls.json | 1 + 3 files changed, 3 insertions(+), 2 deletions(-) diff --git a/.vscode/launch.json b/.vscode/launch.json index 005b4436e259..eaa660486eab 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -155,7 +155,7 @@ "stopOnEntry": false, "sourceMaps": true, "args": [ - "./out/test/**/*.unit.test.js", + "./out/test/**/createNewFileCommand.unit.test.js", "--require=out/test/unittests.js", "--ui=tdd", "--recursive", diff --git a/package.json b/package.json index 34295b61a07f..b5a173076d3c 100644 --- a/package.json +++ b/package.json @@ -324,7 +324,7 @@ ], "commands": [ { - "title": "New Python File", + "title": "%python.command.python.createNewFile.title%", "category": "Python", "command": "python.createNewFile", "when": "config.python.createNewFileEnabled" diff --git a/package.nls.json b/package.nls.json index 775598c7fcb5..e449065f284e 100644 --- a/package.nls.json +++ b/package.nls.json @@ -2,6 +2,7 @@ "python.command.python.sortImports.title": "Sort Imports", "python.command.python.startREPL.title": "Start REPL", "python.command.python.createTerminal.title": "Create Terminal", + "python.command.python.createNewFile.title": "New Python File", "python.command.python.execInTerminal.title": "Run Python File in Terminal", "python.command.python.debugInTerminal.title": "Debug Python File", "python.command.python.execInTerminalIcon.title": "Run Python File", From abb6bcfb1d452f9f73d0a88cd355cf01b5aae4ff Mon Sep 17 00:00:00 2001 From: luabud Date: Wed, 16 Feb 2022 18:34:15 -0800 Subject: [PATCH 14/18] Add titles to package.nls.json --- package.json | 2 +- package.nls.json | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/package.json b/package.json index b5a173076d3c..c7b7b6235eb5 100644 --- a/package.json +++ b/package.json @@ -1881,7 +1881,7 @@ ], "file/newFile": [ { - "title": "Python File", + "title": "%python.menu.createNewFile.title%", "command": "python.createNewFile", "category": "file", "when": "config.python.createNewFileEnabled" diff --git a/package.nls.json b/package.nls.json index e449065f284e..1d737a94770b 100644 --- a/package.nls.json +++ b/package.nls.json @@ -30,6 +30,7 @@ "python.command.python.analysis.restartLanguageServer.title": "Restart Language Server", "python.command.python.launchTensorBoard.title": "Launch TensorBoard", "python.command.python.refreshTensorBoard.title": "Refresh TensorBoard", + "python.menu.createNewFile.title": "Python File", "python.snippet.launch.standard.label": "Python: Current File", "python.snippet.launch.module.label": "Python: Module", "python.snippet.launch.module.default": "enter-your-module-name", From 31ddd390be195fafecddb92275449cf67ddb0931 Mon Sep 17 00:00:00 2001 From: luabud Date: Wed, 16 Feb 2022 18:35:40 -0800 Subject: [PATCH 15/18] Remove accidental launch.json edit --- .vscode/launch.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.vscode/launch.json b/.vscode/launch.json index eaa660486eab..005b4436e259 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -155,7 +155,7 @@ "stopOnEntry": false, "sourceMaps": true, "args": [ - "./out/test/**/createNewFileCommand.unit.test.js", + "./out/test/**/*.unit.test.js", "--require=out/test/unittests.js", "--ui=tdd", "--recursive", From 49b1599853d9cce554d8e62badd1c75bf376aa43 Mon Sep 17 00:00:00 2001 From: Luciana Abud <45497113+luabud@users.noreply.github.com> Date: Tue, 22 Feb 2022 08:01:34 -0800 Subject: [PATCH 16/18] Apply suggestions from code review Co-authored-by: Kartik Raj --- package.json | 2 +- .../application/commands/createNewFileCommand.unit.test.ts | 3 +++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/package.json b/package.json index c7b7b6235eb5..6925de45c3cd 100644 --- a/package.json +++ b/package.json @@ -512,7 +512,7 @@ }, "python.createNewFileEnabled": { "default": "false", - "description": "Enable the Python: New Python File command.", + "description": "Enable the `Python: New Python File` command.", "scope": "machine", "type": "boolean", "tags": [ diff --git a/src/test/common/application/commands/createNewFileCommand.unit.test.ts b/src/test/common/application/commands/createNewFileCommand.unit.test.ts index 89b7f272dbc1..ce090bfe637d 100644 --- a/src/test/common/application/commands/createNewFileCommand.unit.test.ts +++ b/src/test/common/application/commands/createNewFileCommand.unit.test.ts @@ -1,3 +1,6 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + import { anything, deepEqual, instance, mock, verify, when } from 'ts-mockito'; import { TextDocument } from 'vscode'; import { Commands } from '../../../../client/common/constants'; From 461fdcd701beec7d24930c9be0da163a588b8622 Mon Sep 17 00:00:00 2001 From: luabud Date: Tue, 22 Feb 2022 09:28:57 -0800 Subject: [PATCH 17/18] Add shorttitle to new file command --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index c7b7b6235eb5..9ae5b5ead262 100644 --- a/package.json +++ b/package.json @@ -325,6 +325,7 @@ "commands": [ { "title": "%python.command.python.createNewFile.title%", + "shortTitle": "%python.menu.createNewFile.title%", "category": "Python", "command": "python.createNewFile", "when": "config.python.createNewFileEnabled" @@ -1881,7 +1882,6 @@ ], "file/newFile": [ { - "title": "%python.menu.createNewFile.title%", "command": "python.createNewFile", "category": "file", "when": "config.python.createNewFileEnabled" From 724862596b28e8c2c336830f8a4988b09c6c1aec Mon Sep 17 00:00:00 2001 From: Luciana Abud <45497113+luabud@users.noreply.github.com> Date: Tue, 22 Feb 2022 13:57:10 -0800 Subject: [PATCH 18/18] Apply suggestions from code review Co-authored-by: Kim-Adeline Miguel <51720070+kimadeline@users.noreply.github.com> --- src/client/common/application/commands/createFileCommand.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/src/client/common/application/commands/createFileCommand.ts b/src/client/common/application/commands/createFileCommand.ts index 8929348f483d..509f1a470ce1 100644 --- a/src/client/common/application/commands/createFileCommand.ts +++ b/src/client/common/application/commands/createFileCommand.ts @@ -22,7 +22,6 @@ export class CreatePythonFileCommandHandler implements IExtensionSingleActivatio this.commandManager.registerCommand(Commands.CreateNewFile, this.createPythonFile, this); } - // eslint-disable-next-line class-methods-use-this public async createPythonFile(): Promise { const newFile = await this.workspaceService.openTextDocument({ language: 'python' }); this.appShell.showTextDocument(newFile);