From 635b28b146956b6868c69ecb595d3f005a817ff3 Mon Sep 17 00:00:00 2001 From: Don Jayamanne Date: Wed, 20 Mar 2024 06:44:31 +1100 Subject: [PATCH] Support dropping of custom from metadata --- .editorconfig | 17 +++ .prettierrc.js | 7 + package-lock.json | 6 +- package.json | 6 +- src/cellTags.ts | 361 ++++++++++++++++++++++++---------------------- src/helper.ts | 27 +++- 6 files changed, 242 insertions(+), 182 deletions(-) create mode 100644 .editorconfig create mode 100644 .prettierrc.js 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 9ed9e9f..c9aa2db 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "vscode-jupyter-cell-tags", - "version": "0.1.7", + "version": "0.1.9", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "vscode-jupyter-cell-tags", - "version": "0.1.7", + "version": "0.1.9", "devDependencies": { "@types/glob": "^7.1.3", "@types/mocha": "^8.2.2", @@ -24,7 +24,7 @@ "webpack-cli": "^4.6.0" }, "engines": { - "vscode": "^1.72.0" + "vscode": "^1.88.0" } }, "node_modules/@babel/code-frame": { diff --git a/package.json b/package.json index c4b44ee..58643a1 100644 --- a/package.json +++ b/package.json @@ -2,7 +2,7 @@ "name": "vscode-jupyter-cell-tags", "displayName": "Jupyter Cell Tags", "description": "Jupyter Cell Tags support for VS Code", - "version": "0.1.8", + "version": "0.1.9", "publisher": "ms-toolsai", "preview": true, "icon": "icon.png", @@ -14,7 +14,7 @@ "name": "Microsoft Corporation" }, "engines": { - "vscode": "^1.72.0" + "vscode": "^1.88.0" }, "categories": [ "Notebooks" @@ -132,4 +132,4 @@ "webpack": "^5.36.2", "webpack-cli": "^4.6.0" } -} \ No newline at end of file +} diff --git a/src/cellTags.ts b/src/cellTags.ts index 4a5a9ef..3b20bd1 100644 --- a/src/cellTags.ts +++ b/src/cellTags.ts @@ -3,206 +3,221 @@ import * as vscode from 'vscode'; import * as json from './json'; +import { getCellTags, updateCellTags } from './helper'; export async function addCellTag(cell: vscode.NotebookCell, tags: string[]) { - const oldTags = cell.metadata.custom?.metadata?.tags ?? []; + const oldTags = getCellTags(cell); const newTags: string[] = []; for (const tag of tags) { if (!oldTags.includes(tag)) { newTags.push(tag); } } - if (newTags.length) { - oldTags.push(...newTags); - } - - const cellMetadata = { ...cell.metadata }; - if (!cellMetadata.custom) { - cellMetadata.custom = {}; - } - - if (!cellMetadata.custom.metadata) { - cellMetadata.custom.metadata = {}; - } - - cellMetadata.custom.metadata.tags = oldTags; - - // create workspace edit to update tag - const edit = new vscode.WorkspaceEdit(); - const nbEdit = vscode.NotebookEdit.updateCellMetadata(cell.index, { - ...cellMetadata, - }); - edit.set(cell.notebook.uri, [nbEdit]); - await vscode.workspace.applyEdit(edit); + if (newTags.length) { + oldTags.push(...newTags); + } + + await updateCellTags(cell, oldTags); } export class CellTagStatusBarProvider implements vscode.NotebookCellStatusBarItemProvider { - provideCellStatusBarItems(cell: vscode.NotebookCell, token: vscode.CancellationToken): vscode.ProviderResult { - const items: vscode.NotebookCellStatusBarItem[] = []; - cell.metadata.custom?.metadata?.tags?.forEach((tag: string) => { - items.push({ - text: '$(close) ' + tag, - tooltip: tag, - command: { - title: `Remove Tag ${tag}`, - command: 'jupyter-cell-tags.removeTag', - arguments: [cell, tag] - }, - alignment: vscode.NotebookCellStatusBarAlignment.Left, - }); - }); - - if (items.length) { - // add insert tag status bar item - items.push({ - text: '$(plus) Tag', - tooltip: 'Add Tag', - command: { - title: 'Add Tag', - command: 'jupyter-cell-tags.addTag', - arguments: [cell] - }, - alignment: vscode.NotebookCellStatusBarAlignment.Left, - }); - } - - return items; - } + provideCellStatusBarItems( + cell: vscode.NotebookCell, + token: vscode.CancellationToken + ): vscode.ProviderResult { + const items: vscode.NotebookCellStatusBarItem[] = []; + getCellTags(cell).forEach((tag: string) => { + items.push({ + text: '$(close) ' + tag, + tooltip: tag, + command: { + title: `Remove Tag ${tag}`, + command: 'jupyter-cell-tags.removeTag', + arguments: [cell, tag] + }, + alignment: vscode.NotebookCellStatusBarAlignment.Left + }); + }); + + if (items.length) { + // add insert tag status bar item + items.push({ + text: '$(plus) Tag', + tooltip: 'Add Tag', + command: { + title: 'Add Tag', + command: 'jupyter-cell-tags.addTag', + arguments: [cell] + }, + alignment: vscode.NotebookCellStatusBarAlignment.Left + }); + } + + 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; + } - if (editor.selections[0].start >= editor.notebook.cellCount) { - return; - } + if (editor.selections[0].start >= editor.notebook.cellCount) { + 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; - - for (const document of vscode.workspace.notebookDocuments) { - for (const cell of document.getCells()) { - if (cell.document.uri.toString() === cellUriStr) { - activeCell = cell; - break; - } - } + 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; - } - } + if (activeCell) { + break; + } + } - return activeCell; - } + return activeCell; + } - return undefined; + return undefined; } export function register(context: vscode.ExtensionContext) { - context.subscriptions.push(vscode.notebooks.registerNotebookCellStatusBarItemProvider('jupyter-notebook', new CellTagStatusBarProvider())); - context.subscriptions.push(vscode.commands.registerCommand('jupyter-cell-tags.removeTag', async (cell: vscode.NotebookCell | string, tag: string) => { - let activeCell: vscode.NotebookCell | undefined; - if (typeof cell === 'string') { - // find active cell - activeCell = getActiveCell(); - tag = cell; - } else { - activeCell = cell; - } - - if (!activeCell) { - return; - } - - let tags = activeCell.metadata.custom.metadata.tags; - if (tags) { - // remove tag from tags - const index = tags.indexOf(tag); - if (index > -1) { - tags.splice(index, 1); + context.subscriptions.push( + vscode.notebooks.registerNotebookCellStatusBarItemProvider('jupyter-notebook', new CellTagStatusBarProvider()) + ); + context.subscriptions.push( + vscode.commands.registerCommand( + 'jupyter-cell-tags.removeTag', + async (cell: vscode.NotebookCell | string, tag: string) => { + let activeCell: vscode.NotebookCell | undefined; + if (typeof cell === 'string') { + // find active cell + activeCell = getActiveCell(); + tag = cell; + } else { + activeCell = cell; + } + + if (!activeCell) { + return; + } + + const tags = getCellTags(activeCell); + // remove tag from tags + const index = tags.indexOf(tag); + if (index > -1) { + tags.splice(index, 1); + await updateCellTags(activeCell, tags); + } } - activeCell.metadata.custom.metadata.tags = tags; - - // create workspace edit to update tag - const edit = new vscode.WorkspaceEdit(); - const nbEdit = vscode.NotebookEdit.updateCellMetadata(activeCell.index, { - ...activeCell.metadata, - }); - edit.set(activeCell.notebook.uri, [nbEdit]); - await vscode.workspace.applyEdit(edit); - } else { - return; - } - })); - - context.subscriptions.push(vscode.commands.registerCommand('jupyter-cell-tags.addTag', async (cell: vscode.NotebookCell | vscode.Uri | undefined) => { - cell = reviveCell(cell); - - if (!cell) { - return; - } - - const tag = await vscode.window.showInputBox({ - placeHolder: 'Type to create a cell tag' - }); - - if (tag) { - await addCellTag(cell, [tag]); - } - })); - - context.subscriptions.push(vscode.commands.registerCommand('jupyter-cell-tags.paramaterize', async (cell: vscode.NotebookCell | vscode.Uri | undefined) => { - cell = reviveCell(cell); - if (!cell) { - return; - } - await addCellTag(cell, ['parameters']); - })); - - context.subscriptions.push(vscode.commands.registerCommand('jupyter-cell-tags.editTagsInJSON', 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 tags = json.findNodeAtLocation(metadata, ['tags']); - if (tags) { - const range = new vscode.Range(document.positionAt(tags.offset), document.positionAt(tags.offset + tags.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 + ) + ); + + context.subscriptions.push( + vscode.commands.registerCommand( + 'jupyter-cell-tags.addTag', + async (cell: vscode.NotebookCell | vscode.Uri | undefined) => { + cell = reviveCell(cell); + + if (!cell) { + return; + } + + const tag = await vscode.window.showInputBox({ + placeHolder: 'Type to create a cell tag' + }); + + if (tag) { + await addCellTag(cell, [tag]); + } + } + ) + ); + + context.subscriptions.push( + vscode.commands.registerCommand( + 'jupyter-cell-tags.paramaterize', + async (cell: vscode.NotebookCell | vscode.Uri | undefined) => { + cell = reviveCell(cell); + if (!cell) { + return; + } + await addCellTag(cell, ['parameters']); + } + ) + ); + + context.subscriptions.push( + vscode.commands.registerCommand( + 'jupyter-cell-tags.editTagsInJSON', + 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 tags = json.findNodeAtLocation(metadata, ['tags']); + if (tags) { + const range = new vscode.Range( + document.positionAt(tags.offset), + document.positionAt(tags.offset + tags.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 + }); + } + } + } + ) + ); +} diff --git a/src/helper.ts b/src/helper.ts index c654b5d..9e03df6 100644 --- a/src/helper.ts +++ b/src/helper.ts @@ -3,6 +3,27 @@ import * as vscode from 'vscode'; -export function getCellTags(cell: vscode.NotebookCell) { - return cell.metadata.custom?.metadata?.tags ?? []; -} \ No newline at end of file +export function getCellTags(cell: vscode.NotebookCell): string[] { + const currentTags = + (useCustomMetadata() ? cell.metadata.custom?.metadata?.tags : cell.metadata.metadata?.tags) ?? []; + return [...currentTags]; +} + +export async function updateCellTags(cell: vscode.NotebookCell, tags: string[]) { + const metadata = JSON.parse(JSON.stringify(cell.metadata)); + if (useCustomMetadata()) { + metadata.custom = metadata.custom || {}; + metadata.custom.tags = tags; + } else { + metadata.metadata = metadata.metadata || {}; + metadata.metadata.tags = tags; + } + const edit = new vscode.WorkspaceEdit(); + const nbEdit = vscode.NotebookEdit.updateCellMetadata(cell.index, metadata); + edit.set(cell.notebook.uri, [nbEdit]); + await vscode.workspace.applyEdit(edit); +} + +function useCustomMetadata() { + return !vscode.workspace.getConfiguration('jupyter').get('experimental.dropCustomMetadata', false); +}