From a67e3abb4c5a57df9b6a1b5dead802e8c97a525e Mon Sep 17 00:00:00 2001 From: Maddy <12754347+MaddyDev@users.noreply.github.com> Date: Wed, 18 Dec 2019 09:08:52 -0800 Subject: [PATCH] add open book command to open any book from local sys (#8632) * add open book command to open any book from local sys * no-floating promises * moved all the loc strings to common loc file * typos --- extensions/notebook/package.json | 16 ++++++ extensions/notebook/package.nls.json | 3 +- extensions/notebook/src/book/bookModel.ts | 20 ++++--- extensions/notebook/src/book/bookTreeItem.ts | 9 ++- extensions/notebook/src/book/bookTreeView.ts | 55 ++++++++++--------- .../notebook/src/common/localizedConstants.ts | 23 ++++++++ extensions/notebook/src/extension.ts | 1 + 7 files changed, 88 insertions(+), 39 deletions(-) diff --git a/extensions/notebook/package.json b/extensions/notebook/package.json index 707a1bb215..7f75924684 100644 --- a/extensions/notebook/package.json +++ b/extensions/notebook/package.json @@ -171,6 +171,15 @@ "dark": "resources/dark/search_inverse.svg", "light": "resources/light/search.svg" } + }, + { + "command": "notebook.command.openBook", + "title": "%title.openJupyterBook%", + "category": "%books-preview-category%", + "icon": { + "dark": "resources/dark/open_notebook_inverse.svg", + "light": "resources/light/open_notebook.svg" + } } ], "languages": [ @@ -312,6 +321,13 @@ "group": "inline" } ], + "view/title": [ + { + "command": "notebook.command.openBook", + "when": "view == bookTreeView", + "group": "navigation" + } + ], "notebook/toolbar": [ { "command": "jupyter.cmd.managePackages", diff --git a/extensions/notebook/package.nls.json b/extensions/notebook/package.nls.json index 2910e02311..f3d6eb599c 100644 --- a/extensions/notebook/package.nls.json +++ b/extensions/notebook/package.nls.json @@ -33,5 +33,6 @@ "title.searchJupyterBook": "Search Book", "title.SavedBooks": "Saved Books", "title.UnsavedBooks": "Unsaved Books", - "title.PreviewLocalizedBook": "Get localized SQL Server 2019 guide" + "title.PreviewLocalizedBook": "Get localized SQL Server 2019 guide", + "title.openJupyterBook": "Open Jupyter Book" } diff --git a/extensions/notebook/src/book/bookModel.ts b/extensions/notebook/src/book/bookModel.ts index bf9fecb3f9..5c66e89f2c 100644 --- a/extensions/notebook/src/book/bookModel.ts +++ b/extensions/notebook/src/book/bookModel.ts @@ -12,11 +12,11 @@ import { maxBookSearchDepth, notebookConfigKey } from '../common/constants'; import * as path from 'path'; import * as fileServices from 'fs'; import * as fs from 'fs-extra'; -import * as nls from 'vscode-nls'; +import * as loc from '../common/localizedConstants'; import { IJupyterBookToc, IJupyterBookSection } from '../contracts/content'; import { isNullOrUndefined } from 'util'; -const localize = nls.loadMessageBundle(); + const fsPromises = fileServices.promises; export class BookModel implements azdata.nb.NavigationProvider { @@ -54,9 +54,13 @@ export class BookModel implements azdata.nb.NavigationProvider { let p = path.join(workspacePath, '**', '_data', 'toc.yml').replace(/\\/g, '/'); let tableOfContentPaths = await glob(p, { deep: maxDepth }); - this._tableOfContentPaths = this._tableOfContentPaths.concat(tableOfContentPaths); - let bookOpened: boolean = this._tableOfContentPaths.length > 0; - vscode.commands.executeCommand('setContext', 'bookOpened', bookOpened); + if (tableOfContentPaths.length > 0) { + this._tableOfContentPaths = this._tableOfContentPaths.concat(tableOfContentPaths); + vscode.commands.executeCommand('setContext', 'bookOpened', true); + } else { + vscode.window.showErrorMessage(loc.errBookInitialize); + } + } catch (error) { console.log(error); } @@ -168,7 +172,7 @@ export class BookModel implements azdata.nb.NavigationProvider { ); notebooks.push(markdown); } else { - let error = localize('missingFileError', "Missing file : {0}", sections[i].title); + let error = loc.missingFileError(sections[i].title); vscode.window.showErrorMessage(error); } } @@ -187,9 +191,9 @@ export class BookModel implements azdata.nb.NavigationProvider { try { return section.reduce((acc, val) => Array.isArray(val.sections) ? acc.concat(val).concat(this.parseJupyterSections(val.sections)) : acc.concat(val), []); } catch (error) { - let err: string = localize('InvalidError.tocFile', "{0}", error); + let err: string = loc.invalidTocFileError(error); if (section.length > 0) { - err = localize('Invalid toc.yml', "Error: {0} has an incorrect toc.yml file", section[0].title); //need to find a way to get title. + err = loc.invalidTocError(section[0].title); } vscode.window.showErrorMessage(err); throw err; diff --git a/extensions/notebook/src/book/bookTreeItem.ts b/extensions/notebook/src/book/bookTreeItem.ts index d3a7c65e47..eda86b434e 100644 --- a/extensions/notebook/src/book/bookTreeItem.ts +++ b/extensions/notebook/src/book/bookTreeItem.ts @@ -6,9 +6,8 @@ import * as vscode from 'vscode'; import * as path from 'path'; import * as fs from 'fs'; -import * as nls from 'vscode-nls'; import { IJupyterBookSection, IJupyterBookToc } from '../contracts/content'; -const localize = nls.loadMessageBundle(); +import * as loc from '../common/localizedConstants'; export enum BookTreeItemType { Book = 'Book', @@ -69,12 +68,12 @@ export class BookTreeItem extends vscode.TreeItem { private setCommand() { if (this.book.type === BookTreeItemType.Notebook) { let pathToNotebook = path.join(this.book.root, 'content', this._uri.concat('.ipynb')); - this.command = { command: this.book.isUntitled ? 'bookTreeView.openUntitledNotebook' : 'bookTreeView.openNotebook', title: localize('openNotebookCommand', "Open Notebook"), arguments: [pathToNotebook], }; + this.command = { command: this.book.isUntitled ? 'bookTreeView.openUntitledNotebook' : 'bookTreeView.openNotebook', title: loc.openNotebookCommand, arguments: [pathToNotebook], }; } else if (this.book.type === BookTreeItemType.Markdown) { let pathToMarkdown = path.join(this.book.root, 'content', this._uri.concat('.md')); - this.command = { command: 'bookTreeView.openMarkdown', title: localize('openMarkdownCommand', "Open Markdown"), arguments: [pathToMarkdown], }; + this.command = { command: 'bookTreeView.openMarkdown', title: loc.openMarkdownCommand, arguments: [pathToMarkdown], }; } else if (this.book.type === BookTreeItemType.ExternalLink) { - this.command = { command: 'bookTreeView.openExternalLink', title: localize('openExternalLinkCommand', "Open External Link"), arguments: [this._uri], }; + this.command = { command: 'bookTreeView.openExternalLink', title: loc.openExternalLinkCommand, arguments: [this._uri], }; } } diff --git a/extensions/notebook/src/book/bookTreeView.ts b/extensions/notebook/src/book/bookTreeView.ts index f6d157d531..2c8ade0e78 100644 --- a/extensions/notebook/src/book/bookTreeView.ts +++ b/extensions/notebook/src/book/bookTreeView.ts @@ -10,12 +10,10 @@ import * as fs from 'fs-extra'; import { IPrompter, QuestionTypes, IQuestion } from '../prompts/question'; import CodeAdapter from '../prompts/adapter'; import { BookTreeItem } from './bookTreeItem'; -import * as nls from 'vscode-nls'; import { isEditorTitleFree } from '../common/utils'; import { BookModel } from './bookModel'; import { Deferred } from '../common/promise'; - -const localize = nls.loadMessageBundle(); +import * as loc from '../common/localizedConstants'; export class BookTreeViewProvider implements vscode.TreeDataProvider { @@ -39,7 +37,7 @@ export class BookTreeViewProvider implements vscode.TreeDataProvider a.uri.fsPath)); + this.initialize(workspaceFolders.map(a => a.uri.fsPath)).catch(e => console.error(e)); this.viewId = view; this.prompter = new CodeAdapter(); @@ -72,19 +70,17 @@ export class BookTreeViewProvider implements vscode.TreeDataProvider 0 && books[0].bookItems) { this.currentBook = books[0]; - this.showPreviewFile(urlToOpen); + await this.showPreviewFile(urlToOpen); } else { await this.initialize([bookPath]); let bookViewer = vscode.window.createTreeView(this.viewId, { showCollapseAll: true, treeDataProvider: this }); this.currentBook = this.books.filter(book => book.bookPath === bookPath)[0]; bookViewer.reveal(this.currentBook.bookItems[0], { expand: vscode.TreeItemCollapsibleState.Expanded, focus: true, select: true }); - this.showPreviewFile(urlToOpen); + await this.showPreviewFile(urlToOpen); } } catch (e) { - vscode.window.showErrorMessage(localize('openBookError', "Open book {0} failed: {1}", - bookPath, - e instanceof Error ? e.message : e)); + vscode.window.showErrorMessage(loc.openFileError(bookPath, e instanceof Error ? e.message : e)); } } @@ -99,7 +95,7 @@ export class BookTreeViewProvider implements vscode.TreeDataProvider { if (this.currentBook.bookPath) { - const allFilesFilter = localize('allFiles', "All Files"); + const allFilesFilter = loc.allFiles; let filter: any = {}; filter[allFilesFilter] = '*'; let uris = await vscode.window.showOpenDialog({ @@ -160,7 +150,7 @@ export class BookTreeViewProvider implements vscode.TreeDataProvider 0) { let pickedFolder = uris[0]; @@ -200,6 +190,23 @@ export class BookTreeViewProvider implements vscode.TreeDataProvider { + const allFilesFilter = loc.allFiles; + let filter: any = {}; + filter[allFilesFilter] = '*'; + let uris = await vscode.window.showOpenDialog({ + filters: filter, + canSelectFiles: false, + canSelectMany: false, + canSelectFolders: true, + openLabel: loc.labelBookFolder + }); + if (uris && uris.length > 0) { + let bookPath = uris[0]; + await this.openBook(bookPath.fsPath); + } + } + private runThrottledAction(resource: string, action: () => void) { const isResourceChange = resource !== this._resource; if (isResourceChange) { @@ -230,9 +237,7 @@ export class BookTreeViewProvider implements vscode.TreeDataProvider { return await this.prompter.promptSingle({ type: QuestionTypes.confirm, - message: localize('confirmReplace', "Folder already exists. Are you sure you want to delete and replace this folder?"), + message: loc.confirmReplace, default: false }); } diff --git a/extensions/notebook/src/common/localizedConstants.ts b/extensions/notebook/src/common/localizedConstants.ts index f4a378ff63..3d7f688b1f 100644 --- a/extensions/notebook/src/common/localizedConstants.ts +++ b/extensions/notebook/src/common/localizedConstants.ts @@ -14,3 +14,26 @@ export const msgNo = localize('msgNo', "No"); // Jupyter Constants /////////////////////////////////////////////////////// export const msgSampleCodeDataFrame = localize('msgSampleCodeDataFrame', "This sample code loads the file into a data frame and shows the first 10 results."); + +// Book view-let constants +export const allFiles = localize('allFiles', "All Files"); +export const labelPickFolder = localize('labelPickFolder', "Pick Folder"); +export const labelBookFolder = localize('labelBookFolder', "Select Book"); +export const confirmReplace = localize('confirmReplace', "Folder already exists. Are you sure you want to delete and replace this folder?"); +export const openNotebookCommand = localize('openNotebookCommand', "Open Notebook"); +export const openMarkdownCommand = localize('openMarkdownCommand', "Open Markdown"); +export const openExternalLinkCommand = localize('openExternalLinkCommand', "Open External Link"); + +export const errBookInitialize = localize('bookInitializeFailed', "Book initialize failed: Failed to recognize the structure."); +export function missingFileError(title: string): string { return localize('missingFileError', "Missing file : {0}", title); } +export function invalidTocFileError(error: string): string { return localize('InvalidError.tocFile', "{0}", error); } +export function invalidTocError(title: string): string { return localize('Invalid toc.yml', "Error: {0} has an incorrect toc.yml file", title); } + +export function openFileError(path: string, error: string): string { return localize('openBookError', "Open book {0} failed: {1}", path, error); } +export function openNotebookError(resource: string, error: string): string { return localize('openNotebookError', "Open notebook {0} failed: {1}", resource, error); } +export function openMarkdownError(resource: string, error: string): string { return localize('openMarkdownError', "Open markdown {0} failed: {1}", resource, error); } +export function openUntitledNotebookError(resource: string, error: string): string { return localize('openUntitledNotebookError', "Open untitled notebook {0} as untitled failed: {1}", resource, error); } +export function openExternalLinkError(resource: string, error: string): string { return localize('openExternalLinkError', "Open link {0} failed: {1}", resource, error); } + + + diff --git a/extensions/notebook/src/extension.ts b/extensions/notebook/src/extension.ts index 98ae93cbc5..cde65112d2 100644 --- a/extensions/notebook/src/extension.ts +++ b/extensions/notebook/src/extension.ts @@ -36,6 +36,7 @@ export async function activate(extensionContext: vscode.ExtensionContext): Promi extensionContext.subscriptions.push(vscode.commands.registerCommand('notebook.command.saveBook', () => untitledBookTreeViewProvider.saveJupyterBooks())); extensionContext.subscriptions.push(vscode.commands.registerCommand('notebook.command.searchBook', () => bookTreeViewProvider.searchJupyterBooks())); extensionContext.subscriptions.push(vscode.commands.registerCommand('notebook.command.searchUntitledBook', () => untitledBookTreeViewProvider.searchJupyterBooks())); + extensionContext.subscriptions.push(vscode.commands.registerCommand('notebook.command.openBook', () => bookTreeViewProvider.openNewBook())); extensionContext.subscriptions.push(vscode.commands.registerCommand('_notebook.command.new', (context?: azdata.ConnectedContext) => { let connectionProfile: azdata.IConnectionProfile = undefined;