Repository: stateful/vscode-runme
Pull request #661 (2023-07-09 17:53:22)

Configurable option to split or toggle editor view action
Solves https://github.com/stateful/runme/issues/321 with a new setting.


Patch:
diff --git a/package.json b/package.json
index 9fe82ca5..838426ad 100644
--- a/package.json
+++ b/package.json
@@ -438,6 +438,19 @@
             "default": 10,
             "markdownDescription": "Default number of rows in notebook terminal"
           },
+          "runme.actions.openViewInEditor": {
+            "type": "string",
+            "default": "split",
+            "enum": [
+              "split",
+              "toggle"
+            ],
+            "enumDescriptions": [
+              "Opens text editor to the side",
+              "Will replace notebook view with text editor"
+            ],
+            "markdownDescription": "Default behavior of Open In Editor action button"
+          },
           "runme.codelens.enable": {
             "type": "boolean",
             "default": true,
@@ -464,7 +477,7 @@
           "runme.app.apiUrl": {
             "type": "string",
             "default": "",
-            "markdownDescription": "Runme App API URL"
+            "markdownDescription": "App API URL"
           },
           "runme.app.enableShare": {
             "type": "boolean",
diff --git a/src/extension/commands/index.ts b/src/extension/commands/index.ts
index 27c311a3..0a7c001b 100644
--- a/src/extension/commands/index.ts
+++ b/src/extension/commands/index.ts
@@ -19,6 +19,8 @@ import {
 import { v4 as uuidv4 } from 'uuid'
 
 import {
+  OpenViewInEditorAction,
+  getActionsOpenViewInEditor,
   getBinaryPath,
   getCLIUseIntegratedRunme,
   getTLSDir,
@@ -209,16 +211,36 @@ export function runCLICommand(
   }
 }
 
+function openDocumentAs(doc: { text?: TextDocument; notebook?: NotebookDocument }) {
+  const openIn = getActionsOpenViewInEditor()
+  switch (openIn) {
+    case OpenViewInEditorAction.enum.toggle:
+      {
+        commands.executeCommand('workbench.action.toggleEditorType')
+      }
+      break
+    default:
+      {
+        if (doc.notebook) {
+          window.showNotebookDocument(doc.notebook, {
+            viewColumn: ViewColumn.Active,
+          })
+        } else if (doc.text) {
+          window.showTextDocument(doc.text, {
+            viewColumn: ViewColumn.Beside,
+          })
+        }
+      }
+      break
+  }
+}
+
 export function openAsRunmeNotebook(doc: NotebookDocument) {
-  window.showNotebookDocument(doc, {
-    viewColumn: ViewColumn.Active,
-  })
+  openDocumentAs({ notebook: doc })
 }
 
 export function openSplitViewAsMarkdownText(doc: TextDocument) {
-  window.showTextDocument(doc, {
-    viewColumn: ViewColumn.Beside,
-  })
+  openDocumentAs({ text: doc })
 }
 
 export async function createNewRunmeNotebook() {
diff --git a/src/utils/configuration.ts b/src/utils/configuration.ts
index 64d2b1c6..e82f7ef5 100644
--- a/src/utils/configuration.ts
+++ b/src/utils/configuration.ts
@@ -8,6 +8,7 @@ import { v4 as uuidv4 } from 'uuid'
 import { getAnnotations, isWindows } from '../extension/utils'
 import { SERVER_PORT } from '../constants'
 
+const ACTIONS_SECTION_NAME = 'runme.actions'
 const SERVER_SECTION_NAME = 'runme.server'
 const TERMINAL_SECTION_NAME = 'runme.terminal'
 const CODELENS_SECTION_NAME = 'runme.codelens'
@@ -15,6 +16,7 @@ const ENV_SECTION_NAME = 'runme.env'
 const CLI_SECTION_NAME = 'runme.cli'
 const APP_SECTION_NAME = 'runme.app'
 
+export const OpenViewInEditorAction = z.enum(['split', 'toggle'])
 export const DEFAULT_TLS_DIR = path.join(os.tmpdir(), 'runme', uuidv4(), 'tls')
 const DEFAULT_WORKSPACE_FILE_ORDER = ['.env.local', '.env']
 const DEFAULT_RUNME_APP_API_URL = 'https://app.runme.dev/graphql'
@@ -22,6 +24,9 @@ const DEFAULT_RUNME_APP_API_URL = 'https://app.runme.dev/graphql'
 type NotebookTerminalValue = keyof typeof configurationSchema.notebookTerminal
 
 const configurationSchema = {
+  actions: {
+    openViewInEditor: OpenViewInEditorAction.default('split'),
+  },
   server: {
     customAddress: z.string().nonempty().optional(),
     binaryPath: z.string().optional(),
@@ -53,6 +58,19 @@ const configurationSchema = {
   },
 }
 
+const getActionsConfigurationValue = <T>(
+  configName: keyof typeof configurationSchema.actions,
+  defaultValue: T
+) => {
+  const configurationSection = workspace.getConfiguration(ACTIONS_SECTION_NAME)
+  const configurationValue = configurationSection.get<T>(configName)!
+  const parseResult = configurationSchema.actions[configName].safeParse(configurationValue)
+  if (parseResult.success) {
+    return parseResult.data as T
+  }
+  return defaultValue
+}
+
 const getServerConfigurationValue = <T>(
   configName: keyof typeof configurationSchema.server,
   defaultValue: T
@@ -214,6 +232,14 @@ const registerExtensionEnvironmentVariables = (context: ExtensionContext): void
   )
 }
 
+const getActionsOpenViewInEditor = () => {
+  type ActionEnum = z.infer<typeof OpenViewInEditorAction>
+  return getActionsConfigurationValue<ActionEnum>(
+    'openViewInEditor',
+    OpenViewInEditorAction.enum.split
+  )
+}
+
 const getEnvWorkspaceFileOrder = (): string[] => {
   return getEnvConfigurationValue('workspaceFileOrder', DEFAULT_WORKSPACE_FILE_ORDER)
 }
@@ -249,6 +275,7 @@ export {
   getCodeLensEnabled,
   registerExtensionEnvironmentVariables,
   getCustomServerAddress,
+  getActionsOpenViewInEditor,
   getEnvWorkspaceFileOrder,
   getEnvLoadWorkspaceFiles,
   getCLIUseIntegratedRunme,


Test patch:
diff --git a/tests/extension/commands/index.test.ts b/tests/extension/commands/index.test.ts
index 14cc5438..daaefdf3 100644
--- a/tests/extension/commands/index.test.ts
+++ b/tests/extension/commands/index.test.ts
@@ -31,6 +31,7 @@ import {
 } from '../../../src/extension/commands'
 import { getTerminalByCell, getAnnotations, openFileAsRunmeNotebook } from '../../../src/extension/utils'
 import {
+  getActionsOpenViewInEditor,
   getBinaryPath,
   getCLIUseIntegratedRunme,
   isNotebookTerminalEnabledForCell
@@ -46,9 +47,11 @@ vi.mock('../../../src/extension/utils', () => ({
   openFileAsRunmeNotebook: vi.fn(),
 }))
 vi.mock('../../../src/utils/configuration', () => ({
+  getActionsOpenViewInEditor: vi.fn(),
   getBinaryPath: vi.fn(),
   isNotebookTerminalEnabledForCell: vi.fn(),
   getCLIUseIntegratedRunme: vi.fn().mockReturnValue(false),
+  OpenViewInEditorAction: { enum: { toggle: 'toggle', split: 'split' } }
 }))
 vi.mock('../../../src/extension/provider/cli', () => ({
   CliProvider: {
@@ -206,16 +209,30 @@ suite('runCliCommand', () => {
   })
 })
 
-test('open markdown as Runme notebook', (file: NotebookDocument) => {
+test('open markdown as Runme notebook split', (file: NotebookDocument) => {
+  vi.mocked(getActionsOpenViewInEditor).mockReturnValue('split' as const)
   openAsRunmeNotebook(file)
   expect(window.showNotebookDocument).toBeCalledWith(file, { viewColumn: undefined })
 })
 
-test('open Runme notebook in text editor', (file: TextDocument) => {
+test('open Runme notebook in text editor split', (file: TextDocument) => {
+  vi.mocked(getActionsOpenViewInEditor).mockReturnValue('split' as const)
   openSplitViewAsMarkdownText(file)
   expect(window.showTextDocument).toBeCalledWith(file, { viewColumn: ViewColumn.Beside })
 })
 
+test('open markdown as Runme notebook toggle', (file: NotebookDocument) => {
+  vi.mocked(getActionsOpenViewInEditor).mockReturnValue('toggle' as const)
+  openAsRunmeNotebook(file)
+  expect(commands.executeCommand).toBeCalledWith('workbench.action.toggleEditorType')
+})
+
+test('open Runme notebook in text editor toggle', (file: TextDocument) => {
+  vi.mocked(getActionsOpenViewInEditor).mockReturnValue('toggle' as const)
+  openSplitViewAsMarkdownText(file)
+  expect(commands.executeCommand).toBeCalledWith('workbench.action.toggleEditorType')
+})
+
 test('stopBackgroundTask if terminal exists', () => {
   vi.mocked(getTerminalByCell).mockReturnValue({ dispose: vi.fn() } as any)
   stopBackgroundTask({} as any)