Repository: stateful/vscode-runme
Pull request #96 (2022-12-14 21:18:07)

Add create new Runme notebook menu item
implements #91

![create](https://user-images.githubusercontent.com/731337/207716437-487fb7f8-95ca-4ffe-a463-050f79a9eaca.gif)


sourishkrout: @christian-bromann does it show up in the `File -> New File` options for you? It does not for me. The idea is not to run it directly from the command palette btw.

![image](https://user-images.githubusercontent.com/250527/207731908-960b933a-428b-4531-ae72-a9c982c30f41.png)

sourishkrout: From the looks of it, the `"file/newFile"` is not in the right place in package.json and it likely needs an activation event.
christian-bromann: @sourishkrout fixed contribution point and also added a link to the Runme docs:

![newnew](https://user-images.githubusercontent.com/731337/207945950-1691ceec-221e-4e8a-b021-fff7bff7b15f.gif)


Patch:
diff --git a/__mocks__/vscode.ts b/__mocks__/vscode.ts
index e4d14b53..6ad122d7 100644
--- a/__mocks__/vscode.ts
+++ b/__mocks__/vscode.ts
@@ -17,6 +17,7 @@ export const Uri = {
 }
 
 export const workspace = {
+  openNotebookDocument: vi.fn().mockReturnValue({ uri: 'new notebook uri' }),
   openTextDocument: vi.fn(),
   registerNotebookSerializer: vi.fn(),
   fs: {
@@ -38,7 +39,8 @@ export const window = {
 }
 
 export const commands = {
-  registerCommand: vi.fn()
+  registerCommand: vi.fn(),
+  executeCommand: vi.fn()
 }
 
 export enum ViewColumn  {
@@ -51,3 +53,10 @@ export const env = {
   },
   openExternal: vi.fn()
 }
+
+export const NotebookData = vi.fn()
+export const NotebookCellData = vi.fn()
+export enum NotebookCellKind {
+  Code = 1,
+  Markup = 2
+}
diff --git a/package.json b/package.json
index 0727ba13..641a5992 100644
--- a/package.json
+++ b/package.json
@@ -55,6 +55,10 @@
   "main": "./out/extension.js",
   "contributes": {
     "commands": [
+      {
+        "command": "runme.new",
+        "title": "Create: New Runme Notebook"
+      },
       {
         "command": "runme.openTerminal",
         "title": "Open Terminal for Background Task"
@@ -123,6 +127,12 @@
           "command": "runme.openSplitViewAsMarkdownText",
           "group": "navigation"
         }
+      ],
+      "file/newFile": [
+        {
+          "command": "runme.new",
+          "group": "notebook"
+        }
       ]
     },
     "notebooks": [
diff --git a/src/extension/commands/index.ts b/src/extension/commands/index.ts
index d4d802c7..4e987923 100644
--- a/src/extension/commands/index.ts
+++ b/src/extension/commands/index.ts
@@ -1,6 +1,9 @@
 import path from 'node:path'
 
-import { NotebookCell, Uri, window, env, NotebookDocument, TextDocument, ViewColumn } from 'vscode'
+import {
+  NotebookCell, Uri, window, env, NotebookDocument, TextDocument, ViewColumn,
+  workspace, NotebookData, commands, NotebookCellData, NotebookCellKind
+} from 'vscode'
 
 import { CliProvider } from '../provider/cli'
 import { getMetadata, getTerminalByCell } from '../utils'
@@ -57,3 +60,24 @@ export function openSplitViewAsMarkdownText (doc: TextDocument) {
     viewColumn: ViewColumn.Beside
   })
 }
+
+export async function createNewRunmeNotebook () {
+  const newNotebook = await workspace.openNotebookDocument('runme', new NotebookData([
+    new NotebookCellData(
+      NotebookCellKind.Markup,
+      '# Runme Notebook\n\nStart writing here...',
+      'markdown'
+    ),
+    new NotebookCellData(
+      NotebookCellKind.Code,
+      'echo "Hello World"',
+      'bash'
+    ),
+    new NotebookCellData(
+      NotebookCellKind.Markup,
+      '_Find more documentation on writing Runme markdown on [runme.dev](https://www.runme.dev/docs/intro)!_',
+      'markdown'
+    )
+  ]))
+  await commands.executeCommand('vscode.openWith', newNotebook.uri, 'runme')
+}
diff --git a/src/extension/extension.ts b/src/extension/extension.ts
index 691a23ef..b0c113dc 100644
--- a/src/extension/extension.ts
+++ b/src/extension/extension.ts
@@ -12,7 +12,8 @@ import {
   copyCellToClipboard,
   openAsRunmeNotebook,
   openSplitViewAsMarkdownText ,
-  stopBackgroundTask
+  stopBackgroundTask,
+  createNewRunmeNotebook
 } from './commands'
 import { Serializer } from './serializer'
 
@@ -42,6 +43,7 @@ export class RunmeExtension {
       commands.registerCommand('runme.stopBackgroundTask', stopBackgroundTask),
       commands.registerCommand('runme.openSplitViewAsMarkdownText', openSplitViewAsMarkdownText),
       commands.registerCommand('runme.openAsRunmeNotebook', openAsRunmeNotebook),
+      commands.registerCommand('runme.new', createNewRunmeNotebook),
     )
   }
 }


Test patch:
diff --git a/tests/extension/commands/index.test.ts b/tests/extension/commands/index.test.ts
index d36324a7..a20b1ee6 100644
--- a/tests/extension/commands/index.test.ts
+++ b/tests/extension/commands/index.test.ts
@@ -1,11 +1,17 @@
+import path from 'node:path'
+
 import { beforeEach, expect, test, vi } from 'vitest'
 import {
-  window, env,
+  window,
+  env,
+  workspace,
+  commands,
   // @ts-expect-error mock feature
   terminal,
   NotebookDocument,
   TextDocument,
   ViewColumn,
+  NotebookCellData
 } from 'vscode'
 
 import {
@@ -14,11 +20,13 @@ import {
   runCLICommand,
   openAsRunmeNotebook,
   openSplitViewAsMarkdownText,
-  stopBackgroundTask } from '../../../src/extension/commands'
+  stopBackgroundTask,
+  createNewRunmeNotebook
+} from '../../../src/extension/commands'
 import { getTerminalByCell, getMetadata } from '../../../src/extension/utils'
 import { CliProvider } from '../../../src/extension/provider/cli'
 
-vi.mock('vscode')
+vi.mock('vscode', () => import(path.join(process.cwd(), '__mocks__', 'vscode')))
 vi.mock('../../../src/extension/utils', () => ({
   getMetadata: vi.fn(),
   getTerminalByCell: vi.fn()
@@ -94,3 +102,10 @@ test('stopBackgroundTask if terminal does not exist', () => {
   stopBackgroundTask({} as any)
   expect(window.showWarningMessage).toBeCalledTimes(1)
 })
+
+test('createNewRunmeNotebook', async () => {
+  await createNewRunmeNotebook()
+  expect(workspace.openNotebookDocument).toBeCalledWith('runme', expect.any(Object))
+  expect(NotebookCellData).toBeCalledTimes(3)
+  expect(commands.executeCommand).toBeCalledWith('vscode.openWith', expect.any(String), 'runme')
+})
diff --git a/tests/extension/extension.test.ts b/tests/extension/extension.test.ts
index 576e55ec..2b6b0d7a 100644
--- a/tests/extension/extension.test.ts
+++ b/tests/extension/extension.test.ts
@@ -12,5 +12,5 @@ test('initializes all providers', async () => {
   await ext.initialize(context)
   expect(notebooks.registerNotebookCellStatusBarItemProvider).toBeCalledTimes(5)
   expect(workspace.registerNotebookSerializer).toBeCalledTimes(1)
-  expect(commands.registerCommand).toBeCalledTimes(7)
+  expect(commands.registerCommand).toBeCalledTimes(8)
 })