diff --git a/extensions/mssql/src/dashboard/bookExtensions.ts b/extensions/mssql/src/dashboard/bookExtensions.ts index 1926e584f7..05c0bfa677 100644 --- a/extensions/mssql/src/dashboard/bookExtensions.ts +++ b/extensions/mssql/src/dashboard/bookExtensions.ts @@ -100,6 +100,8 @@ class AzdataExtensionBookContributionProvider extends Disposable implements Book this._onContributionsChanged.fire(this); } }, undefined, this._disposables); + + this.registerCommands(); } private readonly _onContributionsChanged = this._register(new vscode.EventEmitter()); @@ -117,8 +119,18 @@ class AzdataExtensionBookContributionProvider extends Disposable implements Book .map(BookContributions.fromExtension) .reduce(BookContributions.merge, []); } + + private registerCommands(): void { + this.contributions.map(book => { + let bookName: string = path.basename(book.path); + vscode.commands.executeCommand('setContext', bookName, true); + vscode.commands.registerCommand('books.' + bookName, async (context) => { + vscode.commands.executeCommand('bookTreeView.openBook', book.path, true); + }); + }); + } } export function getBookExtensionContributions(context: vscode.ExtensionContext): BookContributionProvider { return new AzdataExtensionBookContributionProvider(context.extensionPath); -} \ No newline at end of file +} diff --git a/extensions/mssql/src/dashboard/bookWidget.ts b/extensions/mssql/src/dashboard/bookWidget.ts index 45c66542dd..72dd35a144 100644 --- a/extensions/mssql/src/dashboard/bookWidget.ts +++ b/extensions/mssql/src/dashboard/bookWidget.ts @@ -5,15 +5,10 @@ import * as vscode from 'vscode'; import * as azdata from 'azdata'; -import * as nls from 'vscode-nls'; -import * as fs from 'fs-extra'; -import * as path from 'path'; import { - BookContributionProvider, BookContribution + BookContributionProvider } from './bookExtensions'; -import * as Utils from '../utils'; -const localize = nls.loadMessageBundle(); export function registerBooksWidget(bookContributionProvider: BookContributionProvider): void { azdata.ui.registerModelViewProvider('books-widget', async (view) => { @@ -40,7 +35,8 @@ export function registerBooksWidget(bookContributionProvider: BookContributionPr title: contribution.name }).component(); tsgbooklink.onDidClick(() => { - promptForFolder(contribution); + let uri: vscode.Uri = vscode.Uri.file(contribution.path); + openBookViewlet(uri); }); bookRow.addItem(tsgbooklink, { CSSStyles: { @@ -73,56 +69,6 @@ export function registerBooksWidget(bookContributionProvider: BookContributionPr }); } -async function promptForFolder(bookContribution: BookContribution): Promise { - try { - const allFilesFilter = localize('allFiles', "All Files"); - let filter = {}; - filter[allFilesFilter] = '*'; - let uris = await vscode.window.showOpenDialog({ - filters: filter, - canSelectFiles: false, - canSelectMany: false, - canSelectFolders: true, - openLabel: localize('labelPickFolder', "Pick Folder") - }); - if (uris && uris.length > 0) { - let pickedFolder = uris[0]; - let destinationUri: vscode.Uri = vscode.Uri.file(path.join(pickedFolder.fsPath, bookContribution.name)); - await saveBooksToFolder(destinationUri, bookContribution); - await promptToReloadWindow(destinationUri); - } - return; - } catch (error) { - vscode.window.showErrorMessage(localize('FailedDuringSaveAndPrompt', 'Failed : {0}', Utils.getErrorMessage(error))); - } +function openBookViewlet(folderUri: vscode.Uri): void { + vscode.commands.executeCommand('bookTreeView.openBook', folderUri.fsPath, true); } - -async function saveBooksToFolder(folderUri: vscode.Uri, bookContribution: BookContribution): Promise { - // Get book contributions - if (bookContribution && folderUri) { - //remove folder if exists - await fs.removeSync(folderUri.fsPath); - //make directory for each contribution book. - await fs.mkdirSync(folderUri.fsPath); - await fs.copy(bookContribution.path, folderUri.fsPath); - } -} -function promptToReloadWindow(folderUri: vscode.Uri): void { - const actionReload = localize('prompt.reloadInstance', "Reload"); - const actionOpenNew = localize('prompt.openNewInstance', "Open new instance"); - vscode.window.showInformationMessage(localize('prompt.reloadDescription', "Reload and view the Jupyter Books in the Files view."), actionReload, actionOpenNew) - .then(selectedAction => { - if (selectedAction === actionReload) { - vscode.commands.executeCommand('workbench.action.setWorkspaceAndOpen', { - forceNewWindow: false, - folderPath: folderUri - }); - } - if (selectedAction === actionOpenNew) { - vscode.commands.executeCommand('workbench.action.setWorkspaceAndOpen', { - forceNewWindow: true, - folderPath: folderUri - }); - } - }); -} \ No newline at end of file diff --git a/extensions/notebook/package.json b/extensions/notebook/package.json index 1754b70fdb..b22ed3c40b 100644 --- a/extensions/notebook/package.json +++ b/extensions/notebook/package.json @@ -1,385 +1,402 @@ { - "name": "notebook", - "displayName": "%displayName%", - "description": "%description%", - "version": "0.1.0", - "publisher": "Microsoft", - "engines": { - "vscode": "*", - "azdata": "*" - }, - "main": "./out/extension", - "activationEvents": [ - "*" - ], - "contributes": { - "configuration": { - "type": "object", - "title": "%notebook.configuration.title%", - "properties": { - "notebook.maxBookSearchDepth": { - "type": "number", - "default": 5, - "description": "%notebook.maxBookSearchDepth.description%" - }, - "notebook.pythonPath": { - "type": "string", - "default": "", - "description": "%notebook.pythonPath.description%" - }, - "notebook.useExistingPython": { - "type": "boolean", - "default": false, - "description": "%notebook.useExistingPython.description%" - }, - "notebook.overrideEditorTheming": { - "type": "boolean", - "default": true, - "description": "%notebook.overrideEditorTheming.description%" - }, - "notebook.maxTableRows": { - "type": "number", - "default": 5000, - "description": "%notebook.maxTableRows.description%" - } - } - }, - "commands": [ - { - "command": "notebook.command.analyzeNotebook", - "title": "%notebook.analyzeJupyterNotebook%" - }, - { - "command": "_notebook.command.new", - "title": "%notebook.command.new%", - "icon": { - "dark": "resources/dark/new_notebook_inverse.svg", - "light": "resources/light/new_notebook.svg" - } - }, - { - "command": "notebook.command.open", - "title": "%notebook.command.open%", - "icon": { - "dark": "resources/dark/open_notebook_inverse.svg", - "light": "resources/light/open_notebook.svg" - } - }, - { - "command": "notebook.command.runactivecell", - "title": "%notebook.command.runactivecell%", - "icon": "resources/dark/touchbar_run_cell.png" - }, - { - "command": "notebook.command.clearactivecellresult", - "title": "%notebook.command.clearactivecellresult%" - }, - { - "command": "notebook.command.runallcells", - "title": "%notebook.command.runallcells%" - }, - { - "command": "notebook.command.addcode", - "title": "%notebook.command.addcode%" - }, - { - "command": "notebook.command.addtext", - "title": "%notebook.command.addtext%" - }, - { - "command": "notebook.command.addcell", - "title": "%notebook.command.addcell%", - "icon": "resources/dark/touchbar_add_cell.png" - }, - { - "command": "jupyter.cmd.analyzeNotebook", - "title": "%title.analyzeJupyterNotebook%" - }, - { - "command": "jupyter.task.newNotebook", - "title": "%title.newJupyterNotebook%", - "icon": { - "dark": "resources/dark/new_notebook_inverse.svg", - "light": "resources/light/new_notebook.svg" - } - }, - { - "command": "jupyter.task.openNotebook", - "title": "%title.openJupyterNotebook%", - "icon": { - "dark": "resources/dark/open_notebook_inverse.svg", - "light": "resources/light/open_notebook.svg" - } - }, - { - "command": "jupyter.cmd.newNotebook", - "title": "%title.newJupyterNotebook%", - "icon": { - "dark": "resources/dark/new_notebook_inverse.svg", - "light": "resources/light/new_notebook.svg" - } - }, - { - "command": "jupyter.cmd.managePackages", - "title": "%title.managePackages%", - "icon": { - "dark": "resources/dark/manage_inverse.svg", - "light": "resources/light/manage.svg" - } - }, - { - "command": "jupyter.cmd.configurePython", - "title": "%title.configurePython%" - }, - { - "command": "jupyter.reinstallDependencies", - "title": "%title.reinstallNotebookDependencies%" - } - ], - "languages": [ - { - "id": "notebook", - "extensions": [ - ".ipynb" - ], - "aliases": [ - "Notebook" - ] - } - ], - "menus": { - "commandPalette": [ - { - "command": "notebook.command.analyzeNotebook", - "when": "false" - }, - { - "command": "_notebook.command.new", - "when": "false" - }, - { - "command": "notebook.command.open" - }, - { - "command": "notebook.command.runactivecell", - "when": "notebookEditorVisible" - }, - { - "command": "notebook.command.clearactivecellresult", - "when": "notebookEditorVisible" - }, - { - "command": "notebook.command.runallcells", - "when": "notebookEditorVisible" - }, - { - "command": "notebook.command.addcode", - "when": "notebookEditorVisible" - }, - { - "command": "notebook.command.addtext", - "when": "notebookEditorVisible" - }, - { - "command": "notebook.command.addcell", - "when": "false" - }, - { - "command": "jupyter.task.newNotebook", - "when": "false" - }, - { - "command": "jupyter.cmd.newNotebook", - "when": "false" - }, - { - "command": "jupyter.cmd.analyzeNotebook", - "when": "false" - }, - { - "command": "jupyter.task.openNotebook", - "when": "false" - }, - { - "command": "jupyter.cmd.managePackages", - "when": "false" - } - ], - "touchBar": [ - { - "command": "notebook.command.runactivecell", - "when": "activeEditor == workbench.editor.notebookEditor", - "group": "1_notebook@1" - }, - { - "command": "notebook.command.addcell", - "when": "activeEditor == workbench.editor.notebookEditor", - "group": "1_notebook@2" - } - ], - "objectExplorer/item/context": [ - { - "command": "notebook.command.analyzeNotebook", - "when": "nodeType=~/^mssqlCluster/ && nodeLabel=~/[^\\s]+(\\.(csv|tsv|txt))$/ && nodeType == mssqlCluster:file", - "group": "1notebook@1" - }, - { - "command": "jupyter.cmd.newNotebook", - "when": "connectionProvider == HADOOP_KNOX && nodeType && nodeType == Server", - "group": "1root@1" - }, - { - "command": "jupyter.cmd.analyzeNotebook", - "when": "nodeType=~/^hdfs/ && nodeLabel=~/[^\\s]+(\\.(csv|tsv|txt))$/ && nodeType == hdfs:file", - "group": "1notebook@1" - } - ], - "notebook/toolbar": [ - { - "command": "jupyter.cmd.managePackages", - "when": "providerId == jupyter && notebook:pythonInstalled" - } - ] - }, - "keybindings": [ - { - "command": "notebook.command.runactivecell", - "key": "F5", - "when": "activeEditor == workbench.editor.notebookEditor" - }, - { - "command": "notebook.command.clearactivecellresult", - "key": "Ctrl+Shift+R", - "when": "activeEditor == workbench.editor.notebookEditor" - }, - { - "command": "notebook.command.runallcells", - "key": "Ctrl+Shift+F5", - "when": "activeEditor == workbench.editor.notebookEditor" - }, - { - "command": "notebook.command.addcode", - "key": "Ctrl+Shift+C", - "when": "activeEditor == workbench.editor.notebookEditor" - }, - { - "command": "notebook.command.addtext", - "key": "Ctrl+Shift+T", - "when": "activeEditor == workbench.editor.notebookEditor" - } - ], - "notebook.languagemagics": [ - { - "magic": "lang_python", - "language": "python", - "executionTarget": null, - "kernels": [ - "sql" - ] - }, - { - "magic": "lang_r", - "language": "r", - "executionTarget": null, - "kernels": [ - "sql" - ] - }, - { - "magic": "lang_java", - "language": "java", - "executionTarget": null, - "kernels": [ - "sql" - ] - } - ], - "notebook.providers": { - "provider": "jupyter", - "fileExtensions": [ - "IPYNB" - ], - "standardKernels": [ - { - "name": "pyspark3kernel", - "displayName": "PySpark3", - "connectionProviderIds": [ - "HADOOP_KNOX", - "MSSQL" - ] - }, - { - "name": "pysparkkernel", - "displayName": "PySpark", - "connectionProviderIds": [ - "HADOOP_KNOX", - "MSSQL" - ] - }, - { - "name": "sparkkernel", - "displayName": "Spark | Scala", - "connectionProviderIds": [ - "HADOOP_KNOX", - "MSSQL" - ] - }, - { - "name": "sparkrkernel", - "displayName": "Spark | R", - "connectionProviderIds": [ - "HADOOP_KNOX", - "MSSQL" - ] - }, - { - "name": "python3", - "displayName": "Python 3", - "connectionProviderIds": [] - } - ] - }, - "views": { - "explorer": [ - { - "id": "bookTreeView", - "name": "Books", - "when": "bookOpened && notebookQuality != stable" - } - ] - } - }, - "dependencies": { - "@jupyterlab/services": "^3.2.1", - "@types/js-yaml": "^3.12.1", - "@types/rimraf": "^2.0.2", - "decompress": "^4.2.0", - "error-ex": "^1.3.1", - "fast-glob": "^3.0.4", - "figures": "^2.0.0", - "fs-extra": "^5.0.0", - "glob": "^7.1.1", - "node-fetch": "^2.3.0", - "request": "^2.88.0", - "temp-write": "^3.4.0", - "vscode-languageclient": "^5.3.0-next.1", - "vscode-nls": "^4.0.0" - }, - "devDependencies": { - "@types/decompress": "^4.2.3", - "@types/fs-extra": "^5.0.0", - "@types/glob": "^7.1.1", - "@types/mocha": "^5.2.5", - "@types/node": "^11.9.3", - "@types/request": "^2.48.1", - "@types/temp-write": "^3.3.0", - "@types/uuid": "^3.4.5", - "assert": "^1.4.1", - "mocha": "^5.2.0", - "mocha-junit-reporter": "^1.17.0", - "mocha-multi-reporters": "^1.1.7", - "typemoq": "^2.1.0", - "vscode": "1.1.5" - }, - "enableProposedApi": true + "name": "notebook", + "displayName": "%displayName%", + "description": "%description%", + "version": "0.1.0", + "publisher": "Microsoft", + "engines": { + "vscode": "*", + "azdata": "*" + }, + "main": "./out/extension", + "activationEvents": [ + "*" + ], + "contributes": { + "configuration": { + "type": "object", + "title": "%notebook.configuration.title%", + "properties": { + "notebook.maxBookSearchDepth": { + "type": "number", + "default": 5, + "description": "%notebook.maxBookSearchDepth.description%" + }, + "notebook.pythonPath": { + "type": "string", + "default": "", + "description": "%notebook.pythonPath.description%" + }, + "notebook.useExistingPython": { + "type": "boolean", + "default": false, + "description": "%notebook.useExistingPython.description%" + }, + "notebook.overrideEditorTheming": { + "type": "boolean", + "default": true, + "description": "%notebook.overrideEditorTheming.description%" + }, + "notebook.maxTableRows": { + "type": "number", + "default": 5000, + "description": "%notebook.maxTableRows.description%" + } + } + }, + "commands": [ + { + "command": "notebook.command.analyzeNotebook", + "title": "%notebook.analyzeJupyterNotebook%" + }, + { + "command": "_notebook.command.new", + "title": "%notebook.command.new%", + "icon": { + "dark": "resources/dark/new_notebook_inverse.svg", + "light": "resources/light/new_notebook.svg" + } + }, + { + "command": "notebook.command.open", + "title": "%notebook.command.open%", + "icon": { + "dark": "resources/dark/open_notebook_inverse.svg", + "light": "resources/light/open_notebook.svg" + } + }, + { + "command": "notebook.command.runactivecell", + "title": "%notebook.command.runactivecell%", + "icon": "resources/dark/touchbar_run_cell.png" + }, + { + "command": "notebook.command.clearactivecellresult", + "title": "%notebook.command.clearactivecellresult%" + }, + { + "command": "notebook.command.runallcells", + "title": "%notebook.command.runallcells%" + }, + { + "command": "notebook.command.addcode", + "title": "%notebook.command.addcode%" + }, + { + "command": "notebook.command.addtext", + "title": "%notebook.command.addtext%" + }, + { + "command": "notebook.command.addcell", + "title": "%notebook.command.addcell%", + "icon": "resources/dark/touchbar_add_cell.png" + }, + { + "command": "jupyter.cmd.analyzeNotebook", + "title": "%title.analyzeJupyterNotebook%" + }, + { + "command": "jupyter.task.newNotebook", + "title": "%title.newJupyterNotebook%", + "icon": { + "dark": "resources/dark/new_notebook_inverse.svg", + "light": "resources/light/new_notebook.svg" + } + }, + { + "command": "jupyter.task.openNotebook", + "title": "%title.openJupyterNotebook%", + "icon": { + "dark": "resources/dark/open_notebook_inverse.svg", + "light": "resources/light/open_notebook.svg" + } + }, + { + "command": "jupyter.cmd.newNotebook", + "title": "%title.newJupyterNotebook%", + "icon": { + "dark": "resources/dark/new_notebook_inverse.svg", + "light": "resources/light/new_notebook.svg" + } + }, + { + "command": "jupyter.cmd.managePackages", + "title": "%title.managePackages%", + "icon": { + "dark": "resources/dark/manage_inverse.svg", + "light": "resources/light/manage.svg" + } + }, + { + "command": "jupyter.cmd.configurePython", + "title": "%title.configurePython%" + }, + { + "command": "jupyter.reinstallDependencies", + "title": "%title.reinstallNotebookDependencies%" + }, + { + "command": "books.sqlserver2019", + "title": "%title.SQL19PreviewBook%", + "category": "%books-preview-category%" + } + ], + "languages": [ + { + "id": "notebook", + "extensions": [ + ".ipynb" + ], + "aliases": [ + "Notebook" + ] + } + ], + "menus": { + "commandPalette": [ + { + "command": "notebook.command.analyzeNotebook", + "when": "false" + }, + { + "command": "_notebook.command.new", + "when": "false" + }, + { + "command": "notebook.command.open" + }, + { + "command": "notebook.command.runactivecell", + "when": "notebookEditorVisible" + }, + { + "command": "notebook.command.clearactivecellresult", + "when": "notebookEditorVisible" + }, + { + "command": "notebook.command.runallcells", + "when": "notebookEditorVisible" + }, + { + "command": "notebook.command.addcode", + "when": "notebookEditorVisible" + }, + { + "command": "notebook.command.addtext", + "when": "notebookEditorVisible" + }, + { + "command": "notebook.command.addcell", + "when": "false" + }, + { + "command": "jupyter.task.newNotebook", + "when": "false" + }, + { + "command": "jupyter.cmd.newNotebook", + "when": "false" + }, + { + "command": "jupyter.cmd.analyzeNotebook", + "when": "false" + }, + { + "command": "jupyter.task.openNotebook", + "when": "false" + }, + { + "command": "jupyter.cmd.managePackages", + "when": "false" + }, + { + "command": "books.sqlserver2019", + "when": "sqlserver2019 && notebookQuality != stable" + } + ], + "touchBar": [ + { + "command": "notebook.command.runactivecell", + "when": "activeEditor == workbench.editor.notebookEditor", + "group": "1_notebook@1" + }, + { + "command": "notebook.command.addcell", + "when": "activeEditor == workbench.editor.notebookEditor", + "group": "1_notebook@2" + } + ], + "objectExplorer/item/context": [ + { + "command": "notebook.command.analyzeNotebook", + "when": "nodeType=~/^mssqlCluster/ && nodeLabel=~/[^\\s]+(\\.(csv|tsv|txt))$/ && nodeType == mssqlCluster:file", + "group": "1notebook@1" + }, + { + "command": "jupyter.cmd.newNotebook", + "when": "connectionProvider == HADOOP_KNOX && nodeType && nodeType == Server", + "group": "1root@1" + }, + { + "command": "jupyter.cmd.analyzeNotebook", + "when": "nodeType=~/^hdfs/ && nodeLabel=~/[^\\s]+(\\.(csv|tsv|txt))$/ && nodeType == hdfs:file", + "group": "1notebook@1" + } + ], + "notebook/toolbar": [ + { + "command": "jupyter.cmd.managePackages", + "when": "providerId == jupyter && notebook:pythonInstalled" + } + ] + }, + "keybindings": [ + { + "command": "notebook.command.runactivecell", + "key": "F5", + "when": "activeEditor == workbench.editor.notebookEditor" + }, + { + "command": "notebook.command.clearactivecellresult", + "key": "Ctrl+Shift+R", + "when": "activeEditor == workbench.editor.notebookEditor" + }, + { + "command": "notebook.command.runallcells", + "key": "Ctrl+Shift+F5", + "when": "activeEditor == workbench.editor.notebookEditor" + }, + { + "command": "notebook.command.addcode", + "key": "Ctrl+Shift+C", + "when": "activeEditor == workbench.editor.notebookEditor" + }, + { + "command": "notebook.command.addtext", + "key": "Ctrl+Shift+T", + "when": "activeEditor == workbench.editor.notebookEditor" + } + ], + "notebook.languagemagics": [ + { + "magic": "lang_python", + "language": "python", + "executionTarget": null, + "kernels": [ + "sql" + ] + }, + { + "magic": "lang_r", + "language": "r", + "executionTarget": null, + "kernels": [ + "sql" + ] + }, + { + "magic": "lang_java", + "language": "java", + "executionTarget": null, + "kernels": [ + "sql" + ] + } + ], + "notebook.providers": { + "provider": "jupyter", + "fileExtensions": [ + "IPYNB" + ], + "standardKernels": [ + { + "name": "pyspark3kernel", + "displayName": "PySpark3", + "connectionProviderIds": [ + "HADOOP_KNOX", + "MSSQL" + ] + }, + { + "name": "pysparkkernel", + "displayName": "PySpark", + "connectionProviderIds": [ + "HADOOP_KNOX", + "MSSQL" + ] + }, + { + "name": "sparkkernel", + "displayName": "Spark | Scala", + "connectionProviderIds": [ + "HADOOP_KNOX", + "MSSQL" + ] + }, + { + "name": "sparkrkernel", + "displayName": "Spark | R", + "connectionProviderIds": [ + "HADOOP_KNOX", + "MSSQL" + ] + }, + { + "name": "python3", + "displayName": "Python 3", + "connectionProviderIds": [] + } + ] + }, + "viewsContainers": { + "activitybar": [ + { + "id": "books-explorer", + "title": "Jupyter Books", + "icon": "resources/dark/open_notebook_inverse.svg" + } + ] + }, + "views": { + "books-explorer": [ + { + "id": "bookTreeView", + "name": "Books" + } + ] + } + }, + "dependencies": { + "@jupyterlab/services": "^3.2.1", + "@types/js-yaml": "^3.12.1", + "@types/rimraf": "^2.0.2", + "decompress": "^4.2.0", + "error-ex": "^1.3.1", + "fast-glob": "^3.0.4", + "figures": "^2.0.0", + "fs-extra": "^5.0.0", + "glob": "^7.1.1", + "node-fetch": "^2.3.0", + "request": "^2.88.0", + "temp-write": "^3.4.0", + "vscode-languageclient": "^5.3.0-next.1", + "vscode-nls": "^4.0.0" + }, + "devDependencies": { + "@types/decompress": "^4.2.3", + "@types/fs-extra": "^5.0.0", + "@types/glob": "^7.1.1", + "@types/mocha": "^5.2.5", + "@types/node": "^11.9.3", + "@types/request": "^2.48.1", + "@types/temp-write": "^3.3.0", + "@types/uuid": "^3.4.5", + "assert": "^1.4.1", + "mocha": "^5.2.0", + "mocha-junit-reporter": "^1.17.0", + "mocha-multi-reporters": "^1.1.7", + "typemoq": "^2.1.0", + "vscode": "1.1.5" + }, + "enableProposedApi": true } diff --git a/extensions/notebook/package.nls.json b/extensions/notebook/package.nls.json index a3766bfa3a..10b64cb3f1 100644 --- a/extensions/notebook/package.nls.json +++ b/extensions/notebook/package.nls.json @@ -26,5 +26,7 @@ "config.jupyter.kernelConfigValuesDescription": "Configuration options for Jupyter kernels. This is automatically managed and not recommended to be manually edited.", "title.reinstallNotebookDependencies": "Reinstall Notebook dependencies", "title.configurePython": "Configure Python for Notebooks", - "title.managePackages": "Manage Packages" + "title.managePackages": "Manage Packages", + "title.SQL19PreviewBook": "SQL Server 2019 Guide", + "books-preview-category": "Jupyter Books" } diff --git a/extensions/notebook/src/book/bookTreeItem.ts b/extensions/notebook/src/book/bookTreeItem.ts index 0a8c80ae33..6895e10ae0 100644 --- a/extensions/notebook/src/book/bookTreeItem.ts +++ b/extensions/notebook/src/book/bookTreeItem.ts @@ -22,6 +22,7 @@ export interface BookTreeItemFormat { tableOfContents: any[]; page: any; type: BookTreeItemType; + treeItemCollapsibleState: number; } export class BookTreeItem extends vscode.TreeItem { @@ -32,10 +33,10 @@ export class BookTreeItem extends vscode.TreeItem { public command: vscode.Command; constructor(public book: BookTreeItemFormat, icons: any) { - super(book.title, vscode.TreeItemCollapsibleState.Collapsed); + super(book.title, book.treeItemCollapsibleState); if (book.type === BookTreeItemType.Book) { - this.collapsibleState = vscode.TreeItemCollapsibleState.Collapsed; + this.collapsibleState = book.treeItemCollapsibleState; this._sections = book.page; } else { this.setPageVariables(); diff --git a/extensions/notebook/src/book/bookTreeView.ts b/extensions/notebook/src/book/bookTreeView.ts index c9885a65cc..fc5f48d130 100644 --- a/extensions/notebook/src/book/bookTreeView.ts +++ b/extensions/notebook/src/book/bookTreeView.ts @@ -11,8 +11,12 @@ import * as yaml from 'js-yaml'; import * as glob from 'fast-glob'; import { BookTreeItem, BookTreeItemType } from './bookTreeItem'; import { maxBookSearchDepth, notebookConfigKey } from '../common/constants'; +import { isEditorTitleFree } from '../common/utils'; import * as nls from 'vscode-nls'; +import { promisify } from 'util'; + const localize = nls.loadMessageBundle(); +const existsAsync = promisify(fs.exists); export class BookTreeViewProvider implements vscode.TreeDataProvider, azdata.nb.NavigationProvider { readonly providerId: string = 'BookNavigator'; @@ -25,17 +29,29 @@ export class BookTreeViewProvider implements vscode.TreeDataProvider = new vscode.EventEmitter(); + private _openAsUntitled: boolean; constructor(workspaceFolders: vscode.WorkspaceFolder[], extensionContext: vscode.ExtensionContext) { - this.getTableOfContentFiles(workspaceFolders).then(() => undefined, (err) => { console.log(err); }); - this._extensionContext = extensionContext; + this.initialze(workspaceFolders, null, extensionContext); + } + + private initialze(workspaceFolders: vscode.WorkspaceFolder[], bookPath: string, context: vscode.ExtensionContext): void { + let workspacePaths: string[] = []; + if (bookPath) { + workspacePaths.push(bookPath); + } + else if (workspaceFolders) { + workspacePaths = workspaceFolders.map(a => a.uri.fsPath); + } + this.getTableOfContentFiles(workspacePaths).then(() => undefined, (err) => { console.log(err); }); + this._extensionContext = context; } public get onReadAllTOCFiles(): vscode.Event { return this._onReadAllTOCFiles.event; } - async getTableOfContentFiles(workspaceFolders: vscode.WorkspaceFolder[]): Promise { + async getTableOfContentFiles(workspacePaths: string[]): Promise { let notebookConfig = vscode.workspace.getConfiguration(notebookConfigKey); let maxDepth = notebookConfig[maxBookSearchDepth]; // Use default value if user enters an invalid value @@ -44,7 +60,6 @@ export class BookTreeViewProvider implements vscode.TreeDataProvider a.uri.fsPath); for (let workspacePath of workspacePaths) { let p = path.join(workspacePath, '**', '_data', 'toc.yml').replace(/\\/g, '/'); let tableOfContentPaths = await glob(p, { deep: maxDepth }); @@ -55,12 +70,50 @@ export class BookTreeViewProvider implements vscode.TreeDataProvider { + try { + // Check if the book is already open in viewlet. + if (this._tableOfContentPaths.indexOf(path.join(bookPath, '_data', 'toc.yml').replace(/\\/g, '/')) > -1 && this._allNotebooks.size > 0) { + vscode.commands.executeCommand('workbench.books.action.focusBooksExplorer'); + } + else { + await this.getTableOfContentFiles([bookPath]); + let bookViewer = vscode.window.createTreeView('bookTreeView', { showCollapseAll: true, treeDataProvider: this }); + await vscode.commands.executeCommand('workbench.books.action.focusBooksExplorer'); + this._openAsUntitled = openAsUntitled; + let books = this.getBooks(); + if (books && books.length > 0) { + bookViewer.reveal(books[0], { expand: vscode.TreeItemCollapsibleState.Expanded, focus: true, select: true }); + const readmeMarkdown: string = path.join(bookPath, 'content', books[0].tableOfContents[0].url.concat('.md')); + const readmeNotebook: string = path.join(bookPath, 'content', books[0].tableOfContents[0].url.concat('.ipynb')); + const markdownExists = await existsAsync(readmeMarkdown); + const notebookExists = await existsAsync(readmeNotebook); + if (markdownExists) { + vscode.commands.executeCommand('markdown.showPreview', vscode.Uri.file(readmeMarkdown)); + } + else if (notebookExists) { + vscode.workspace.openTextDocument(readmeNotebook); + } + } + } + } catch (e) { + vscode.window.showErrorMessage(localize('openBookError', "Open book {0} failed: {1}", + bookPath, + e instanceof Error ? e.message : e)); + } + } + async openNotebook(resource: string): Promise { try { - let doc = await vscode.workspace.openTextDocument(resource); - vscode.window.showTextDocument(doc); + if (this._openAsUntitled) { + this.openNotebookAsUntitled(resource); + } + else { + let doc = await vscode.workspace.openTextDocument(resource); + vscode.window.showTextDocument(doc); + } } catch (e) { - vscode.window.showErrorMessage(localize('openNotebookError', 'Open file {0} failed: {1}', + vscode.window.showErrorMessage(localize('openNotebookError', "Open file {0} failed: {1}", resource, e instanceof Error ? e.message : e)); } @@ -78,6 +131,24 @@ export class BookTreeViewProvider implements vscode.TreeDataProvider { + let initialContent = document.getText(); + azdata.nb.showNotebookDocument(untitledFileName, { + connectionProfile: null, + initialContent: initialContent, + initialDirtyState: false + }); + }); + } catch (e) { + vscode.window.showErrorMessage(localize('openUntitledNotebookError', "Open file {0} as untitled failed: {1}", + resource, + e instanceof Error ? e.message : e)); + } + } + private runThrottledAction(resource: string, action: () => void) { const isResourceChange = resource !== this._resource; if (isResourceChange) { @@ -101,7 +172,7 @@ export class BookTreeViewProvider implements vscode.TreeDataProvider bookTreeViewProvider.openBook(resource, extensionContext, openAsReadonly))); + extensionContext.subscriptions.push(vscode.commands.registerCommand('bookTreeView.openNotebookAsUntitled', (resource) => bookTreeViewProvider.openNotebookAsUntitled(resource))); extensionContext.subscriptions.push(vscode.commands.registerCommand('bookTreeView.openNotebook', (resource) => bookTreeViewProvider.openNotebook(resource))); extensionContext.subscriptions.push(vscode.commands.registerCommand('bookTreeView.openMarkdown', (resource) => bookTreeViewProvider.openMarkdown(resource))); extensionContext.subscriptions.push(vscode.commands.registerCommand('bookTreeView.openExternalLink', (resource) => bookTreeViewProvider.openExternalLink(resource))); diff --git a/src/sql/workbench/api/browser/mainThreadNotebookDocumentsAndEditors.ts b/src/sql/workbench/api/browser/mainThreadNotebookDocumentsAndEditors.ts index d377ff56c2..bf6ff8b271 100644 --- a/src/sql/workbench/api/browser/mainThreadNotebookDocumentsAndEditors.ts +++ b/src/sql/workbench/api/browser/mainThreadNotebookDocumentsAndEditors.ts @@ -16,6 +16,7 @@ import { ITextEditorOptions } from 'vs/platform/editor/common/editor'; import { Schemas } from 'vs/base/common/network'; import { ConnectionProfile } from 'sql/platform/connection/common/connectionProfile'; import * as types from 'vs/base/common/types'; +import * as fs from 'fs'; import { SqlMainContext, MainThreadNotebookDocumentsAndEditorsShape, SqlExtHostContext, ExtHostNotebookDocumentsAndEditorsShape, @@ -698,13 +699,25 @@ export class MainThreadNotebookDocumentsAndEditors extends Disposable implements onNext: async (uri) => { let result = await this._proxy.$getNavigation(handle, uri); if (result) { - this.doOpenEditor(result.next, {}); + if (uri.scheme === Schemas.untitled) { + let untitledNbName: URI = URI.parse(`untitled:${path.basename(result.next.path, '.ipynb')}`); + this.doOpenEditor(untitledNbName, { initialContent: fs.readFileSync(result.next.path).toString(), initialDirtyState: false }); + } + else { + this.doOpenEditor(result.next, {}); + } } }, onPrevious: async (uri) => { let result = await this._proxy.$getNavigation(handle, uri); if (result) { - this.doOpenEditor(result.previous, {}); + if (uri.scheme === Schemas.untitled) { + let untitledNbName: URI = URI.parse(`untitled:${path.basename(result.previous.path, '.ipynb')}`); + this.doOpenEditor(untitledNbName, { initialContent: fs.readFileSync(result.previous.path).toString(), initialDirtyState: false }); + } + else { + this.doOpenEditor(result.previous, {}); + } } } }); diff --git a/src/sql/workbench/parts/notebook/browser/notebook.common.contribution.ts b/src/sql/workbench/parts/notebook/browser/notebook.common.contribution.ts index 4e9aaa2748..e73da3d346 100644 --- a/src/sql/workbench/parts/notebook/browser/notebook.common.contribution.ts +++ b/src/sql/workbench/parts/notebook/browser/notebook.common.contribution.ts @@ -23,6 +23,7 @@ import { IViewletService } from 'vs/workbench/services/viewlet/browser/viewlet'; import { URI } from 'vs/base/common/uri'; import { IWorkspaceEditingService } from 'vs/workbench/services/workspace/common/workspaceEditing'; import { IWindowService } from 'vs/platform/windows/common/windows'; +import { IViewContainersRegistry, Extensions as ViewContainerExtensions, ViewContainer } from 'vs/workbench/common/views'; import { ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey'; import { NodeContextKey } from 'sql/workbench/parts/dataExplorer/common/nodeContext'; import { MssqlNodeContext } from 'sql/workbench/parts/dataExplorer/common/mssqlNodeContext'; @@ -157,6 +158,22 @@ configurationRegistry.registerConfiguration({ } }); +/** +* Explorer viewlet id. +*/ +export const VIEWLET_ID = 'bookTreeView'; +/** +* Explorer viewlet container. +*/ +export const VIEW_CONTAINER: ViewContainer = Registry.as(ViewContainerExtensions.ViewContainersRegistry).registerViewContainer(VIEWLET_ID); +registerAction({ + id: 'workbench.books.action.focusBooksExplorer', + handler: async (accessor) => { + const viewletService = accessor.get(IViewletService); + viewletService.openViewlet('workbench.view.extension.books-explorer', true); + } +}); + /* *************** Output components *************** */ // Note: most existing types use the same component to render. In order to // preserve correct rank order, we register it once for each different rank of