mirror of
https://github.com/ckaczor/azuredatastudio.git
synced 2026-02-09 01:32:34 -05:00
Nb/open book without reload (#6635)
* initial commit * fix: added initialize method to fix the previous/next links rendering * added checks * open first markdown/ipynb in the book ans expand the view * added launch book from command pallete, removed the pick folder and save option added launching the first notebook/markdown * moved the open book command pallet action from mssql to notebooks * open as untitled to true * opening markdown files issue fix * removed opening as untitled files * open ipynb files as untitled and changes for previous&next links to work * add books as seperate viewlet * localize double quote issues and renamed treeCollapsibleState * renames and added logic to conditionally show Preview command * moved registerCommands from widget to extension contribution * isEditorUntitled check * async comment updates * formatting issues. * promisfying the async calls * moved existsAsync to top
This commit is contained in:
@@ -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<BookTreeItem>, azdata.nb.NavigationProvider {
|
||||
readonly providerId: string = 'BookNavigator';
|
||||
@@ -25,17 +29,29 @@ export class BookTreeViewProvider implements vscode.TreeDataProvider<BookTreeIte
|
||||
private _throttleTimer: any;
|
||||
private _resource: string;
|
||||
private _onReadAllTOCFiles: vscode.EventEmitter<void> = new vscode.EventEmitter<void>();
|
||||
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<void> {
|
||||
return this._onReadAllTOCFiles.event;
|
||||
}
|
||||
|
||||
async getTableOfContentFiles(workspaceFolders: vscode.WorkspaceFolder[]): Promise<void> {
|
||||
async getTableOfContentFiles(workspacePaths: string[]): Promise<void> {
|
||||
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<BookTreeIte
|
||||
} else if (maxDepth === 0) { // No limit of search depth if user enters 0
|
||||
maxDepth = undefined;
|
||||
}
|
||||
let workspacePaths: string[] = workspaceFolders.map(a => 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<BookTreeIte
|
||||
this._onReadAllTOCFiles.fire();
|
||||
}
|
||||
|
||||
async openBook(bookPath: string, context: vscode.ExtensionContext, openAsUntitled: boolean): Promise<void> {
|
||||
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<void> {
|
||||
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<BookTreeIte
|
||||
});
|
||||
}
|
||||
|
||||
openNotebookAsUntitled(resource: string): void {
|
||||
try {
|
||||
let untitledFileName: vscode.Uri = this.getUntitledNotebookUri(resource);
|
||||
vscode.workspace.openTextDocument(resource).then((document) => {
|
||||
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<BookTreeIte
|
||||
try {
|
||||
vscode.env.openExternal(vscode.Uri.parse(resource));
|
||||
} catch (e) {
|
||||
vscode.window.showErrorMessage(localize('openExternalLinkError', 'Open link {0} failed: {1}',
|
||||
vscode.window.showErrorMessage(localize('openExternalLinkError', "Open link {0} failed: {1}",
|
||||
resource,
|
||||
e instanceof Error ? e.message : e));
|
||||
}
|
||||
@@ -139,7 +210,8 @@ export class BookTreeViewProvider implements vscode.TreeDataProvider<BookTreeIte
|
||||
root: root,
|
||||
tableOfContents: this.flattenArray(tableOfContents),
|
||||
page: tableOfContents,
|
||||
type: BookTreeItemType.Book
|
||||
type: BookTreeItemType.Book,
|
||||
treeItemCollapsibleState: vscode.TreeItemCollapsibleState.Expanded,
|
||||
},
|
||||
{
|
||||
light: this._extensionContext.asAbsolutePath('resources/light/book.svg'),
|
||||
@@ -148,7 +220,7 @@ export class BookTreeViewProvider implements vscode.TreeDataProvider<BookTreeIte
|
||||
);
|
||||
books.push(book);
|
||||
} catch (e) {
|
||||
vscode.window.showErrorMessage(localize('openConfigFileError', 'Open file {0} failed: {1}',
|
||||
vscode.window.showErrorMessage(localize('openConfigFileError', "Open file {0} failed: {1}",
|
||||
path.join(root, '_config.yml'),
|
||||
e instanceof Error ? e.message : e));
|
||||
}
|
||||
@@ -166,7 +238,8 @@ export class BookTreeViewProvider implements vscode.TreeDataProvider<BookTreeIte
|
||||
root: root,
|
||||
tableOfContents: tableOfContents,
|
||||
page: sections[i],
|
||||
type: BookTreeItemType.ExternalLink
|
||||
type: BookTreeItemType.ExternalLink,
|
||||
treeItemCollapsibleState: vscode.TreeItemCollapsibleState.Collapsed
|
||||
},
|
||||
{
|
||||
light: this._extensionContext.asAbsolutePath('resources/light/link.svg'),
|
||||
@@ -186,7 +259,8 @@ export class BookTreeViewProvider implements vscode.TreeDataProvider<BookTreeIte
|
||||
root: root,
|
||||
tableOfContents: tableOfContents,
|
||||
page: sections[i],
|
||||
type: BookTreeItemType.Notebook
|
||||
type: BookTreeItemType.Notebook,
|
||||
treeItemCollapsibleState: vscode.TreeItemCollapsibleState.Collapsed
|
||||
},
|
||||
{
|
||||
light: this._extensionContext.asAbsolutePath('resources/light/notebook.svg'),
|
||||
@@ -195,13 +269,17 @@ export class BookTreeViewProvider implements vscode.TreeDataProvider<BookTreeIte
|
||||
);
|
||||
notebooks.push(notebook);
|
||||
this._allNotebooks.set(pathToNotebook, notebook);
|
||||
if (this._openAsUntitled) {
|
||||
this._allNotebooks.set(path.basename(pathToNotebook, '.ipynb'), notebook);
|
||||
}
|
||||
} else if (fs.existsSync(pathToMarkdown)) {
|
||||
let markdown = new BookTreeItem({
|
||||
title: sections[i].title,
|
||||
root: root,
|
||||
tableOfContents: tableOfContents,
|
||||
page: sections[i],
|
||||
type: BookTreeItemType.Markdown
|
||||
type: BookTreeItemType.Markdown,
|
||||
treeItemCollapsibleState: vscode.TreeItemCollapsibleState.Collapsed
|
||||
},
|
||||
{
|
||||
light: this._extensionContext.asAbsolutePath('resources/light/markdown.svg'),
|
||||
@@ -210,7 +288,7 @@ export class BookTreeViewProvider implements vscode.TreeDataProvider<BookTreeIte
|
||||
);
|
||||
notebooks.push(markdown);
|
||||
} else {
|
||||
vscode.window.showErrorMessage(localize('missingFileError', 'Missing file : {0}', sections[i].title));
|
||||
vscode.window.showErrorMessage(localize('missingFileError', "Missing file : {0}", sections[i].title));
|
||||
}
|
||||
}
|
||||
} else {
|
||||
@@ -226,8 +304,8 @@ export class BookTreeViewProvider implements vscode.TreeDataProvider<BookTreeIte
|
||||
if (notebook) {
|
||||
result = {
|
||||
hasNavigation: true,
|
||||
previous: notebook.previousUri ? vscode.Uri.file(notebook.previousUri) : undefined,
|
||||
next: notebook.nextUri ? vscode.Uri.file(notebook.nextUri) : undefined
|
||||
previous: notebook.previousUri ? this._openAsUntitled ? vscode.Uri.parse(notebook.previousUri).with({ scheme: 'untitled' }) : vscode.Uri.file(notebook.previousUri) : undefined,
|
||||
next: notebook.nextUri ? this._openAsUntitled ? vscode.Uri.parse(notebook.nextUri).with({ scheme: 'untitled' }) : vscode.Uri.file(notebook.nextUri) : undefined
|
||||
};
|
||||
} else {
|
||||
result = {
|
||||
@@ -239,4 +317,28 @@ export class BookTreeViewProvider implements vscode.TreeDataProvider<BookTreeIte
|
||||
return Promise.resolve(result);
|
||||
}
|
||||
|
||||
getUntitledNotebookUri(resource: string): vscode.Uri {
|
||||
let title = this.findNextUntitledFileName(resource);
|
||||
let untitledFileName: vscode.Uri = vscode.Uri.parse(`untitled:${title}`);
|
||||
if (!this._allNotebooks.get(untitledFileName.fsPath)) {
|
||||
let notebook = this._allNotebooks.get(resource);
|
||||
this._allNotebooks.set(untitledFileName.fsPath, notebook);
|
||||
}
|
||||
return untitledFileName;
|
||||
}
|
||||
|
||||
findNextUntitledFileName(filePath: string): string {
|
||||
const fileExtension = path.extname(filePath);
|
||||
const baseName = path.basename(filePath, fileExtension);
|
||||
let idx = 0;
|
||||
let title = `${baseName}`;
|
||||
do {
|
||||
const suffix = idx === 0 ? '' : `-${idx}`;
|
||||
title = `${baseName}${suffix}`;
|
||||
idx++;
|
||||
} while (!isEditorTitleFree(title));
|
||||
|
||||
return title;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user