Skip to content

Commit

Permalink
Revert "removed Notebook creation from extension since it is moving t…
Browse files Browse the repository at this point in the history
…o built-in (#9311)"

This reverts commit 502897a.
  • Loading branch information
amunger committed Mar 16, 2022
1 parent 17f9744 commit 1460507
Show file tree
Hide file tree
Showing 10 changed files with 229 additions and 5 deletions.
1 change: 0 additions & 1 deletion news/2 Fixes/9250.md

This file was deleted.

25 changes: 21 additions & 4 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@
"theme": "light"
},
"engines": {
"vscode": "^1.66.0-insider"
"vscode": "^1.64.0"
},
"keywords": [
"jupyter",
Expand Down Expand Up @@ -55,6 +55,7 @@
"onCommand:jupyter.exportToHTML",
"onCommand:jupyter.exportToPDF",
"onCommand:jupyter.notebookeditor.export",
"onCommand:jupyter.createnewnotebook",
"onCommand:jupyter.createnewinteractive",
"onCommand:jupyter.importnotebook",
"onCommand:jupyter.importnotebookfile",
Expand Down Expand Up @@ -88,15 +89,15 @@
"when": "false",
"steps": [
{
"id": "ipynb.newUntitledIpynb",
"id": "jupyter.createNewNotebook",
"title": "Create or open a Jupyter Notebook",
"description": "Right click in the file explorer and create a new file with an .ipynb extension. Or, open the [Command Palette](command:workbench.action.showCommands) and run the command \n``Create: New Jupyter Notebook``.\n[Create New Jupyter Notebook](command:toSide:ipynb.newUntitledIpynb)\n If you have an existing project, you can also [open a folder](command:toSide:workbench.action.files.openFolder) and/or clone a project from GitHub: [clone a Git repository](command:toSide:git.clone).",
"description": "Right click in the file explorer and create a new file with an .ipynb extension. Or, open the [Command Palette](command:workbench.action.showCommands) and run the command \n``Jupyter: Create New Jupyter Notebook``.\n[Create New Jupyter Notebook](command:toSide:jupyter.createnewnotebook)\n If you have an existing project, you can also [open a folder](command:toSide:workbench.action.files.openFolder) and/or clone a project from GitHub: [clone a Git repository](command:toSide:git.clone).",
"media": {
"svg": "resources/walkthroughs/opennotebook.svg",
"altText": "Creating a new Jupyter notebook"
},
"completionEvents": [
"onCommand:ipynb.newUntitledIpynb",
"onCommand:jupyter.createnewnotebook",
"onCommand:jupyter.createnewinteractive",
"onCommand:workbench.action.files.openFolder",
"onCommand:workbench.action.files.openFileFolder"
Expand Down Expand Up @@ -701,6 +702,12 @@
"title": "%jupyter.command.jupyter.addcellbelow.title%",
"category": "Jupyter"
},
{
"command": "jupyter.createnewnotebook",
"title": "%jupyter.command.jupyter.createnewnotebook.title%",
"shortTitle": "%jupyter.command.jupyter.createnewnotebook.shortTitle%",
"category": "Jupyter"
},
{
"command": "jupyter.scrolltocell",
"title": "%jupyter.command.jupyter.scrolltocell.title%",
Expand Down Expand Up @@ -1030,6 +1037,11 @@
"group": "Jupyter"
}
],
"file/newFile": [
{
"command": "jupyter.createnewnotebook"
}
],
"commandPalette": [
{
"command": "jupyter.replayPylanceLog",
Expand Down Expand Up @@ -1373,6 +1385,11 @@
"category": "Jupyter",
"when": "jupyter.hascodecells && jupyter.ispythonornativeactive"
},
{
"command": "jupyter.createnewnotebook",
"title": "%jupyter.command.jupyter.createnewnotebook.title%",
"category": "Jupyter"
},
{
"command": "jupyter.runtoline",
"category": "Jupyter",
Expand Down
2 changes: 2 additions & 0 deletions package.nls.json
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,8 @@
"jupyter.command.jupyter.collapseallcells.shorttitle": "Collapse",
"jupyter.command.jupyter.addcellbelow.title": "Add Empty Cell to File",
"jupyter.command.jupyter.scrolltocell.title": "Scroll Cell Into View",
"jupyter.command.jupyter.createnewnotebook.title": "Create New Jupyter Notebook",
"jupyter.command.jupyter.createnewnotebook.shortTitle": "Jupyter Notebook",
"jupyter.command.jupyter.selectJupyterInterpreter.title": "Select Interpreter to start Jupyter server",
"jupyter.command.jupyter.createGitHubIssue.title": "Create GitHub Issue",
"jupyter.command.jupyter.submitGitHubIssue.title": "Submit GitHub Issue",
Expand Down
1 change: 1 addition & 0 deletions package.nls.zh-cn.json
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,7 @@
"jupyter.command.jupyter.collapseallcells.shorttitle": "折叠",
"jupyter.command.jupyter.addcellbelow.title": "添加空单元到文件",
"jupyter.command.jupyter.scrolltocell.title": "滚动至单元",
"jupyter.command.jupyter.createnewnotebook.title": "创建新的空白 Jupyter 笔记本",
"jupyter.command.jupyter.selectJupyterInterpreter.title": "选择解释器来启动 Jupyter 服务器",
"jupyter.command.jupyter.createGitHubIssue.title": "创建 GitHub Issue",
"jupyter.command.jupyter.submitGitHubIssue.title": "提交 GitHub Issue",
Expand Down
1 change: 1 addition & 0 deletions package.nls.zh-tw.json
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,7 @@
"jupyter.command.jupyter.runfromline.title": "在互動式視窗中執行到此行",
"jupyter.command.jupyter.execSelectionInteractive.title": "在互動式視窗中執行選取內容/行",
"jupyter.command.jupyter.clearSavedJupyterUris.title": "清除遠端 Jupyter 伺服器清單",
"jupyter.command.jupyter.createnewnotebook.title": "新增 Jupyter Notebook",
"jupyter.command.jupyter.openOutlineView.title": "顯示目錄(大綱欄位)",
"jupyter.command.jupyter.openOutlineView.shorttitle": "大綱",
"jupyter.command.jupyter.debug.title": "偵錯",
Expand Down
1 change: 1 addition & 0 deletions src/client/common/application/commands.ts
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ interface ICommandNameWithoutArgumentTypeMapping {
[DSCommands.CollapseAllCells]: [];
[DSCommands.ExportOutputAsNotebook]: [];
[DSCommands.AddCellBelow]: [];
[DSCommands.CreateNewNotebook]: [];
[DSCommands.EnableDebugLogging]: [];
[DSCommands.ResetLoggingLevel]: [];
[DSCommands.OpenVariableView]: [];
Expand Down
7 changes: 7 additions & 0 deletions src/interactive-window/commands/commandRegistry.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import { DebugProtocol } from 'vscode-debugprotocol';
import { IShowDataViewerFromVariablePanel } from '../../extension/messageTypes';
import { IKernelProvider } from '../../kernels/types';
import { convertDebugProtocolVariableToIJupyterVariable } from '../../kernels/variables/debuggerVariables';
import { NotebookCreator } from '../../notebooks/notebookCreator';
import { DataViewerChecker } from '../../webviews/dataviewer/dataViewerChecker';
import { ICommandNameArgumentTypeMapping } from '../../client/common/application/commands';
import {
Expand Down Expand Up @@ -65,6 +66,7 @@ export class CommandRegistry implements IDisposable {
@inject(IDataViewerFactory) private readonly dataViewerFactory: IDataViewerFactory,
@inject(IJupyterServerUriStorage) private readonly serverUriStorage: IJupyterServerUriStorage,
@inject(IJupyterVariables) @named(Identifiers.DEBUGGER_VARIABLES) private variableProvider: IJupyterVariables,
@inject(NotebookCreator) private readonly nativeNotebookCreator: NotebookCreator,
@inject(IWorkspaceService) private readonly workspace: IWorkspaceService,
@inject(IInteractiveWindowProvider) private readonly interactiveWindowProvider: IInteractiveWindowProvider,
@inject(IDataScienceErrorHandler) private readonly errorHandler: IDataScienceErrorHandler,
Expand Down Expand Up @@ -94,6 +96,7 @@ export class CommandRegistry implements IDisposable {
this.registerCommand(Commands.GotoNextCellInFile, this.gotoNextCellInFile);
this.registerCommand(Commands.GotoPrevCellInFile, this.gotoPrevCellInFile);
this.registerCommand(Commands.AddCellBelow, this.addCellBelow);
this.registerCommand(Commands.CreateNewNotebook, this.createNewNotebook);
this.registerCommand(Commands.ViewJupyterOutput, this.viewJupyterOutput);
this.registerCommand(Commands.LatestExtension, this.openPythonExtensionPage);
this.registerCommand(Commands.EnableDebugLogging, this.enableDebugLogging);
Expand Down Expand Up @@ -479,6 +482,10 @@ export class CommandRegistry implements IDisposable {
}
}

private async createNewNotebook(): Promise<void> {
await this.nativeNotebookCreator.createNewNotebook();
}

private viewJupyterOutput() {
this.jupyterOutput.show(true);
}
Expand Down
58 changes: 58 additions & 0 deletions src/notebooks/notebookCreator.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.

import { inject, injectable } from 'inversify';
import { QuickPickItem } from 'vscode';
import { IApplicationShell } from '../client/common/application/types';
import { PYTHON_LANGUAGE, JVSC_EXTENSION_ID, JVSC_EXTENSION_DisplayName } from '../client/common/constants';
import { DataScience } from '../client/common/utils/localize';
import { INotebookEditorProvider } from '../client/datascience/types';
import { sendTelemetryEvent } from '../client/telemetry';
import { Telemetry } from '../datascience-ui/common/constants';
import { CreationOptionService } from '../kernels/common/creationOptionsService';

@injectable()
export class NotebookCreator {
constructor(
@inject(INotebookEditorProvider) private readonly editorProvider: INotebookEditorProvider,
@inject(IApplicationShell) private readonly appShell: IApplicationShell,
@inject(CreationOptionService) private readonly creationOptionsService: CreationOptionService
) {}

public async createNewNotebook() {
if (this.creationOptionsService.registrations.length === 0) {
await this.editorProvider.createNew();
return;
}

const items: (QuickPickItem & {
extensionId: string;
defaultCellLanguage: string;
})[] = this.creationOptionsService.registrations.map((item) => {
return {
label: item.displayName,
detail: item.extensionId,
extensionId: item.extensionId,
defaultCellLanguage: item.defaultCellLanguage
};
});

// First item is the Jupyter extension.
items.splice(0, 0, {
defaultCellLanguage: PYTHON_LANGUAGE,
detail: JVSC_EXTENSION_ID,
extensionId: JVSC_EXTENSION_ID,
label: JVSC_EXTENSION_DisplayName
});
const placeHolder = DataScience.placeHolderToSelectOptionForNotebookCreation();
const item = await this.appShell.showQuickPick(items, {
matchOnDescription: true,
matchOnDetail: true,
placeHolder
});
sendTelemetryEvent(Telemetry.OpenNotebookSelection, undefined, { extensionId: item?.extensionId });
if (item) {
await this.editorProvider.createNew({ defaultCellLanguage: item.defaultCellLanguage });
}
}
}
4 changes: 4 additions & 0 deletions src/notebooks/serviceRegistry.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import { NotebookCellBangInstallDiagnosticsProvider } from '../intellisense/diag
import { EmptyNotebookCellLanguageService } from '../intellisense/emptyNotebookCellLanguageService';
import { IntellisenseProvider } from '../intellisense/intellisenseProvider';
import { PythonKernelCompletionProvider } from '../intellisense/pythonKernelCompletionProvider';
import { CreationOptionService } from '../kernels/common/creationOptionsService';
import { KernelProvider } from '../kernels/kernelProvider';
import { IKernelProvider } from '../kernels/types';
import { KernelFilterService } from './controllers/kernelFilter/kernelFilterService';
Expand All @@ -21,6 +22,7 @@ import { NotebookControllerManager } from './controllers/notebookControllerManag
import { RemoteSwitcher } from './controllers/remoteSwitcher';
import { CellOutputDisplayIdTracker } from './execution/cellDisplayIdTracker';
import { NotebookCommandListener } from './notebookCommandListener';
import { NotebookCreator } from './notebookCreator';
import { NotebookEditorProvider } from './notebookEditorProvider';
import { ErrorRendererCommunicationHandler } from './outputs/errorRendererComms';
import { PlotSaveHandler } from './outputs/plotSaveHandler';
Expand Down Expand Up @@ -55,6 +57,8 @@ export function registerTypes(serviceManager: IServiceManager) {
);
serviceManager.addSingleton<INotebookLanguageClientProvider>(INotebookLanguageClientProvider, IntellisenseProvider);
serviceManager.addBinding(INotebookLanguageClientProvider, IExtensionSingleActivationService);
serviceManager.addSingleton<CreationOptionService>(CreationOptionService, CreationOptionService);
serviceManager.addSingleton<NotebookCreator>(NotebookCreator, NotebookCreator);
serviceManager.addSingleton<INotebookControllerManager>(INotebookControllerManager, NotebookControllerManager);
serviceManager.addSingleton<PlotSaveHandler>(PlotSaveHandler, PlotSaveHandler);
serviceManager.addSingleton<PlotViewHandler>(PlotViewHandler, PlotViewHandler);
Expand Down
134 changes: 134 additions & 0 deletions src/test/datascience/notebook/creation/notebookCreation.vscode.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,134 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.

'use strict';

/* eslint-disable @typescript-eslint/no-require-imports, @typescript-eslint/no-var-requires */
import { assert } from 'chai';
import * as sinon from 'sinon';
import { commands } from 'vscode';
import { IApplicationShell, IVSCodeNotebook } from '../../../../client/common/application/types';
import { PYTHON_LANGUAGE } from '../../../../client/common/constants';
import { traceInfo } from '../../../../client/common/logger';
import { IDisposable } from '../../../../client/common/types';
import { Commands } from '../../../../client/datascience/constants';
import { CreationOptionService } from '../../../../kernels/common/creationOptionsService';
import { IExtensionTestApi, waitForCondition } from '../../../common';
import { IS_REMOTE_NATIVE_TEST } from '../../../constants';
import { closeActiveWindows, initialize } from '../../../initialize';
import {
closeNotebooksAndCleanUpAfterTests,
ensureNewNotebooksHavePythonCells,
workAroundVSCodeNotebookStartPages
} from '../helper';

/* eslint-disable @typescript-eslint/no-explicit-any, no-invalid-this */
suite('DataScience - VSCode Notebook - (Creation Integration)', function () {
this.timeout(15_000);
let api: IExtensionTestApi;
let vscodeNotebook: IVSCodeNotebook;
let creationOptions: CreationOptionService;
const disposables: IDisposable[] = [];
suiteSetup(async function () {
api = await initialize();
if (IS_REMOTE_NATIVE_TEST) {
return this.skip();
}
creationOptions = api.serviceContainer.get<CreationOptionService>(CreationOptionService);
vscodeNotebook = api.serviceContainer.get<IVSCodeNotebook>(IVSCodeNotebook);
creationOptions.clear();
await workAroundVSCodeNotebookStartPages();
await ensureNewNotebooksHavePythonCells();
});
teardown(async function () {
traceInfo(`Ended Test ${this.currentTest?.title}`);
sinon.restore();
creationOptions.clear();
await closeNotebooksAndCleanUpAfterTests(disposables);
traceInfo(`Ended Test (completed) ${this.currentTest?.title}`);
});
setup(async function () {
traceInfo(`Start Test ${this.currentTest?.title}`);
sinon.restore();
await closeNotebooksAndCleanUpAfterTests(disposables);
traceInfo(`Start Test (completed) ${this.currentTest?.title}`);
});
suiteTeardown(() => {
try {
creationOptions.clear();
} catch {
//
}
});
teardown(async function () {
traceInfo(`End Test ${this.currentTest?.title}`);
await closeNotebooksAndCleanUpAfterTests(disposables);
traceInfo(`Ended Test (completed) ${this.currentTest?.title}`);
});
async function createNotebookAndValidateLanguageOfFirstCell(expectedLanguage: string) {
await commands.executeCommand(Commands.CreateNewNotebook);
await waitForCondition(async () => !!vscodeNotebook.activeNotebookEditor, 10_000, 'New Notebook not created');
assert.strictEqual(
vscodeNotebook.activeNotebookEditor!.document.cellAt(0).document.languageId.toLowerCase(),
expectedLanguage
);
}
test('With 3rd party integration, display quick pick when selecting create blank notebook command', async function () {
await creationOptions.registerNewNotebookContent('javascript');
assert.equal(creationOptions.registrations.length, 1);
assert.isUndefined(vscodeNotebook.activeNotebookEditor);

const appShell = api.serviceContainer.get<IApplicationShell>(IApplicationShell);
const stub = sinon.stub(appShell, 'showQuickPick').callsFake((items: any) => {
traceInfo(`Quick Pick displayed to user`);
assert.isAtLeast(items.length, 2);

// If this is the first time this prompt was displayed, then select the second item (javascript).
if (stub.callCount === 1) {
return items[1];
}

// Pick the first item, that will be us.
return items[0];
});
disposables.push({ dispose: () => stub.restore() });

// Create a blank notebook & we should have a javascript cell.
await createNotebookAndValidateLanguageOfFirstCell('javascript');
assert.equal(stub.callCount, 1);

await closeActiveWindows();

// Try again & this time select the first item from the list & we should end up with a python notebook.
await createNotebookAndValidateLanguageOfFirstCell(PYTHON_LANGUAGE.toLowerCase());
assert.equal(stub.callCount, 2);
});
test('Without 3rd party integration, do not display quick pick when selecting create blank notebook command', async function () {
assert.equal(creationOptions.registrations.length, 0);
assert.isUndefined(vscodeNotebook.activeNotebookEditor);

// Create a blank notebook & it should just work.
await createNotebookAndValidateLanguageOfFirstCell(PYTHON_LANGUAGE.toLowerCase());
});
test('Create javascript & powershell Notebook using API', async function () {
// See https://github.com/microsoft/vscode-jupyter/issues/9158
this.skip();
await api.createBlankNotebook({ defaultCellLanguage: 'javascript' });

await waitForCondition(async () => !!vscodeNotebook.activeNotebookEditor, 10_000, 'New Notebook not created');
assert.strictEqual(
vscodeNotebook.activeNotebookEditor!.document.cellAt(0).document.languageId.toLowerCase(),
'javascript'
);

await closeActiveWindows();

await api.createBlankNotebook({ defaultCellLanguage: 'powershell' });

await waitForCondition(async () => !!vscodeNotebook.activeNotebookEditor, 10_000, 'New Notebook not created');
assert.strictEqual(
vscodeNotebook.activeNotebookEditor!.document.cellAt(0).document.languageId.toLowerCase(),
'powershell'
);
});
});

0 comments on commit 1460507

Please sign in to comment.