diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..4f92a46 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,17 @@ +# EditorConfig is awesome: http://EditorConfig.org + +# top-most EditorConfig file +root = true + +# Tab indentation +[*] +indent_style = space +indent_size = 4 +trim_trailing_whitespace = true +insert_final_newline = true + +# The indent size used in the `package.json` file cannot be changed +# https://github.com/npm/npm/pull/3180#issuecomment-16336516 +[{.travis.yml,npm-shrinkwrap.json,package.json}] +indent_style = space +indent_size = 4 diff --git a/.prettierrc.js b/.prettierrc.js new file mode 100644 index 0000000..94969e8 --- /dev/null +++ b/.prettierrc.js @@ -0,0 +1,7 @@ +module.exports = { + singleQuote: true, + printWidth: 120, + tabWidth: 4, + endOfLine: 'auto', + trailingComma: 'none', +}; diff --git a/package-lock.json b/package-lock.json index 21aab4b..7051e7b 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "vscode-jupyter-slideshow", - "version": "0.1.5", + "version": "0.1.6", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "vscode-jupyter-slideshow", - "version": "0.1.5", + "version": "0.1.6", "devDependencies": { "@types/glob": "^7.1.3", "@types/mocha": "^8.2.2", @@ -24,7 +24,7 @@ "webpack-cli": "^4.6.0" }, "engines": { - "vscode": "^1.71.0" + "vscode": "^1.88.0" } }, "node_modules/@babel/code-frame": { diff --git a/package.json b/package.json index 42af02f..404ca4b 100644 --- a/package.json +++ b/package.json @@ -2,7 +2,7 @@ "name": "vscode-jupyter-slideshow", "displayName": "Jupyter Slide Show", "description": "Jupyter Slide Show support for VS Code", - "version": "0.1.5", + "version": "0.1.6", "publisher": "ms-toolsai", "preview": true, "icon": "icon.png", @@ -14,7 +14,7 @@ "name": "Microsoft Corporation" }, "engines": { - "vscode": "^1.71.0" + "vscode": "^1.88.0" }, "categories": [ "Notebooks" @@ -79,4 +79,4 @@ "webpack": "^5.36.2", "webpack-cli": "^4.6.0" } -} \ No newline at end of file +} diff --git a/src/slideshow.ts b/src/slideshow.ts index 8da4fc9..08fa503 100644 --- a/src/slideshow.ts +++ b/src/slideshow.ts @@ -5,155 +5,207 @@ import * as vscode from 'vscode'; import * as json from './json'; enum SlideShowType { - slide = 'slide', - subslide = 'subslide', - fragment = 'fragment', - skip = 'skip', - notes = 'notes', - none = 'none' + slide = 'slide', + subslide = 'subslide', + fragment = 'fragment', + skip = 'skip', + notes = 'notes', + none = 'none' } - export class CellSlideShowStatusBarProvider implements vscode.NotebookCellStatusBarItemProvider { - provideCellStatusBarItems(cell: vscode.NotebookCell, token: vscode.CancellationToken): vscode.ProviderResult { - const items: vscode.NotebookCellStatusBarItem[] = []; - const slideshow = cell.metadata.custom?.metadata?.slideshow; - - if (slideshow?.slide_type) { - items.push({ - text: `Slide Type: ${slideshow.slide_type}`, - tooltip: `Slide Type: ${slideshow.slide_type}`, - command: 'jupyter-slideshow.switchSlideType', - alignment: vscode.NotebookCellStatusBarAlignment.Right, - }); - } - - return items; - } + provideCellStatusBarItems( + cell: vscode.NotebookCell, + token: vscode.CancellationToken + ): vscode.ProviderResult { + const items: vscode.NotebookCellStatusBarItem[] = []; + const slideType = getSlideType(cell); + + if (slideType) { + items.push({ + text: `Slide Type: ${slideType}`, + tooltip: `Slide Type: ${slideType}`, + command: 'jupyter-slideshow.switchSlideType', + alignment: vscode.NotebookCellStatusBarAlignment.Right + }); + } + + return items; + } } export function getActiveCell() { - // find active cell - const editor = vscode.window.activeNotebookEditor; - if (!editor) { - return; - } + // find active cell + const editor = vscode.window.activeNotebookEditor; + if (!editor) { + return; + } - return editor.notebook.cellAt(editor.selections[0].start); + return editor.notebook.cellAt(editor.selections[0].start); } export function reviveCell(args: vscode.NotebookCell | vscode.Uri | undefined): vscode.NotebookCell | undefined { - if (!args) { - return getActiveCell(); - } + if (!args) { + return getActiveCell(); + } - if (args && 'index' in args && 'kind' in args && 'notebook' in args && 'document' in args) { + if (args && 'index' in args && 'kind' in args && 'notebook' in args && 'document' in args) { return args as vscode.NotebookCell; } - if (args && 'scheme' in args && 'path' in args) { - const cellUri = vscode.Uri.from(args); - const cellUriStr = cellUri.toString(); - let activeCell: vscode.NotebookCell | undefined = undefined; + if (args && 'scheme' in args && 'path' in args) { + const cellUri = vscode.Uri.from(args); + const cellUriStr = cellUri.toString(); + let activeCell: vscode.NotebookCell | undefined = undefined; + + for (const document of vscode.workspace.notebookDocuments) { + for (const cell of document.getCells()) { + if (cell.document.uri.toString() === cellUriStr) { + activeCell = cell; + break; + } + } + + if (activeCell) { + break; + } + } + + return activeCell; + } - for (const document of vscode.workspace.notebookDocuments) { - for (const cell of document.getCells()) { - if (cell.document.uri.toString() === cellUriStr) { - activeCell = cell; - break; - } - } + return undefined; +} - if (activeCell) { - break; - } - } +export function register(context: vscode.ExtensionContext) { + context.subscriptions.push( + vscode.notebooks.registerNotebookCellStatusBarItemProvider( + 'jupyter-notebook', + new CellSlideShowStatusBarProvider() + ) + ); + + context.subscriptions.push( + vscode.commands.registerCommand( + 'jupyter-slideshow.switchSlideType', + async (cell: vscode.NotebookCell | vscode.Uri | undefined) => { + cell = reviveCell(cell); + if (!cell) { + return; + } + + // create quick pick items for each slide type + const items: vscode.QuickPickItem[] = []; + for (const type in SlideShowType) { + items.push({ + label: type + }); + } + + // show quick pick + const selected = await vscode.window.showQuickPick(items); + // update cell metadata with this slide type + if (selected) { + const selectedType = selected.label === SlideShowType.none ? undefined : selected.label; + await updateSlideType(cell, selectedType); + } + } + ) + ); + + context.subscriptions.push( + vscode.commands.registerCommand( + 'jupyter-slideshow.editSlideShowInJSON', + async (cell: vscode.NotebookCell | vscode.Uri | undefined) => { + cell = reviveCell(cell); + if (!cell) { + return; + } + + const resourceUri = cell.notebook.uri; + const document = await vscode.workspace.openTextDocument(resourceUri); + const tree = json.parseTree(document.getText()); + const cells = json.findNodeAtLocation(tree, ['cells']); + if (cells && cells.children && cells.children[cell.index]) { + const cellNode = cells.children[cell.index]; + const metadata = json.findNodeAtLocation(cellNode, ['metadata']); + if (metadata) { + const slideshow = json.findNodeAtLocation(metadata, ['slideshow']); + if (slideshow) { + const range = new vscode.Range( + document.positionAt(slideshow.offset), + document.positionAt(slideshow.offset + slideshow.length) + ); + await vscode.window.showTextDocument(document, { + selection: range, + viewColumn: vscode.ViewColumn.Beside + }); + } else { + const range = new vscode.Range( + document.positionAt(metadata.offset), + document.positionAt(metadata.offset + metadata.length) + ); + await vscode.window.showTextDocument(document, { + selection: range, + viewColumn: vscode.ViewColumn.Beside + }); + } + } else { + const range = new vscode.Range( + document.positionAt(cellNode.offset), + document.positionAt(cellNode.offset + cellNode.length) + ); + await vscode.window.showTextDocument(document, { + selection: range, + viewColumn: vscode.ViewColumn.Beside + }); + } + } + } + ) + ); +} - return activeCell; - } +export function getSlideType(cell: vscode.NotebookCell): SlideShowType | undefined { + const slideshow: { slide_type: SlideShowType } | undefined = + (useCustomMetadata() ? cell.metadata.custom?.metadata?.slideshow : cell.metadata.metadata?.slideshow) ?? + undefined; + return slideshow?.slide_type; +} +export async function updateSlideType(cell: vscode.NotebookCell, slideType?: string) { + if (!slideType && !getSlideType(cell)) { + return; + } - return undefined; + const metadata = JSON.parse(JSON.stringify(cell.metadata)); + if (useCustomMetadata()) { + metadata.custom = metadata.custom || {}; + metadata.custom.metadata = metadata.custom.metadata || {}; + if (!slideType) { + if (metadata.custom.metadata.slideshow) { + delete metadata.custom.metadata.slideshow; + } + } else { + metadata.custom.metadata.slideshow = metadata.custom.metadata.slideshow || {}; + metadata.custom.metadata.slideshow.slide_type = slideType; + } + } else { + metadata.metadata = metadata.metadata || {}; + if (!slideType) { + if (metadata.metadata.slideshow) { + delete metadata.metadata.slideshow; + } + } else { + metadata.metadata.slideshow = metadata.metadata.slideshow || {}; + metadata.metadata.slideshow.slide_type = slideType; + } + } + const edit = new vscode.WorkspaceEdit(); + const nbEdit = vscode.NotebookEdit.updateCellMetadata(cell.index, metadata); + edit.set(cell.notebook.uri, [nbEdit]); + await vscode.workspace.applyEdit(edit); } -export function register(context: vscode.ExtensionContext) { - context.subscriptions.push(vscode.notebooks.registerNotebookCellStatusBarItemProvider('jupyter-notebook', new CellSlideShowStatusBarProvider())); - - context.subscriptions.push(vscode.commands.registerCommand('jupyter-slideshow.switchSlideType', async (cell: vscode.NotebookCell | vscode.Uri | undefined) => { - cell = reviveCell(cell); - if (!cell) { - return; - } - - // create quick pick items for each slide type - const items: vscode.QuickPickItem[] = []; - for (const type in SlideShowType) { - items.push({ - label: type - }); - } - - // show quick pick - const selected = await vscode.window.showQuickPick(items); - // updat cell metadata with this slide type - if (selected) { - const cellMetadataCopy = { ...cell.metadata }; - if (selected.label === SlideShowType.none) { - // remove the slideshow metadata - delete cellMetadataCopy.custom?.metadata?.slideshow; - } else { - if (!cellMetadataCopy.custom) { - cellMetadataCopy.custom = {}; - } - - if (cellMetadataCopy.custom.metadata) { - cellMetadataCopy.custom.metadata.slideshow = cellMetadataCopy.custom.metadata.slideshow ?? {}; - cellMetadataCopy.custom.metadata.slideshow.slide_type = selected.label; - } else { - // no metadata, so create it - cellMetadataCopy.custom.metadata = { - slideshow: { - slide_type: selected.label - } - }; - } - } - - // create workspace edit to update slideshow - const edit = new vscode.WorkspaceEdit(); - const nbEdit = vscode.NotebookEdit.updateCellMetadata(cell.index, { - ...cellMetadataCopy, - }); - edit.set(cell.notebook.uri, [nbEdit]); - await vscode.workspace.applyEdit(edit); - } - })); - - context.subscriptions.push(vscode.commands.registerCommand('jupyter-slideshow.editSlideShowInJSON', async (cell: vscode.NotebookCell | vscode.Uri | undefined) => { - cell = reviveCell(cell); - if (!cell) { - return; - } - - const resourceUri = cell.notebook.uri; - const document = await vscode.workspace.openTextDocument(resourceUri); - const tree = json.parseTree(document.getText()); - const cells = json.findNodeAtLocation(tree, ['cells']); - if (cells && cells.children && cells.children[cell.index]) { - const cellNode = cells.children[cell.index]; - const metadata = json.findNodeAtLocation(cellNode, ['metadata']); - if (metadata) { - const slideshow = json.findNodeAtLocation(metadata, ['slideshow']); - if (slideshow) { - const range = new vscode.Range(document.positionAt(slideshow.offset), document.positionAt(slideshow.offset + slideshow.length)); - await vscode.window.showTextDocument(document, { selection: range, viewColumn: vscode.ViewColumn.Beside }); - } else { - const range = new vscode.Range(document.positionAt(metadata.offset), document.positionAt(metadata.offset + metadata.length)); - await vscode.window.showTextDocument(document, { selection: range, viewColumn: vscode.ViewColumn.Beside }); - } - } else { - const range = new vscode.Range(document.positionAt(cellNode.offset), document.positionAt(cellNode.offset + cellNode.length)); - await vscode.window.showTextDocument(document, { selection: range, viewColumn: vscode.ViewColumn.Beside }); - } - } - })); -} \ No newline at end of file +function useCustomMetadata() { + return !vscode.workspace.getConfiguration('jupyter').get('experimental.dropCustomMetadata', false); +}