Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Optionally focus a cell when using bootstrapping #1860

Merged
merged 1 commit into from
Dec 15, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion src/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1129,8 +1129,9 @@ export enum WebViews {
NotebookEnvStore = 'runme.notebook.envStore',
}
export const CATEGORY_SEPARATOR = ','
export const FOCUS_CELL_STORAGE_KEY = 'focusCell'
export const EXECUTION_CELL_STORAGE_KEY = 'executionCell'
export const EXECUTION_CELL_CREATION_DATE_STORAGE_KEY = 'executionCellCreationDate'
export const CELL_CREATION_DATE_STORAGE_KEY = 'cellCreationDate'
export const SAVE_CELL_LOGIN_CONSENT_STORAGE_KEY = 'loginConsent'
export const GITHUB_USER_SIGNED_IN = 'userSignedIn'
export const PLATFORM_USER_SIGNED_IN = 'platformUserSignedIn'
Expand Down
6 changes: 3 additions & 3 deletions src/extension/extension.ts
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@ import GrpcRunner, { IRunner } from './runner'
import * as survey from './survey'
import { RunmeCodeLensProvider } from './provider/codelens'
import CloudPanel from './panels/cloud'
import { createDemoFileRunnerForActiveNotebook, createDemoFileRunnerWatcher } from './handler/utils'
import { createBootFileRunnerForActiveNotebook, createBootFileRunnerWatcher } from './handler/utils'
import { GithubAuthProvider } from './provider/githubAuth'
import { StatefulAuthProvider } from './provider/statefulAuth'
import { IPanel } from './panels/base'
Expand Down Expand Up @@ -346,8 +346,8 @@ export class RunmeExtension {
RunmeExtension.registerCommand('runme.addToRecommendedExtensions', () =>
addToRecommendedExtension(context),
),
createDemoFileRunnerForActiveNotebook(context, kernel),
createDemoFileRunnerWatcher(context, kernel),
createBootFileRunnerForActiveNotebook(context, kernel),
createBootFileRunnerWatcher(context, kernel),
RunmeExtension.registerCommand(
'runme.notebookOutputsMasked',
this.handleMasking(kernel, true).bind(this),
Expand Down
4 changes: 2 additions & 2 deletions src/extension/handler/uri.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ import {
parseParams,
writeDemoBootstrapFile,
executeActiveNotebookCell,
setCurrentCellExecutionDemo,
setCurrentCellForBootFile,
} from './utils'

const REGEX_WEB_RESOURCE = /^https?:\/\//
Expand Down Expand Up @@ -115,7 +115,7 @@ export class RunmeUriHandler implements UriHandler, Disposable {
const isProjectOpened =
workspace.workspaceFolders?.length &&
workspace.workspaceFolders.some((w) => w.uri.path === projectPath.path)
await setCurrentCellExecutionDemo(this.context, cell)
await setCurrentCellForBootFile(this.context, { cell, focus: true, execute: true })
if (!isProjectOpened) {
await writeDemoBootstrapFile(projectPath, fileToOpen, cell)
await commands.executeCommand('vscode.openFolder', projectPath, {
Expand Down
89 changes: 71 additions & 18 deletions src/extension/handler/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,9 @@ import { BOOTFILE, BOOTFILE_DEMO } from '../constants'
import { Kernel } from '../kernel'
import getLogger from '../logger'
import {
EXECUTION_CELL_CREATION_DATE_STORAGE_KEY,
CELL_CREATION_DATE_STORAGE_KEY,
EXECUTION_CELL_STORAGE_KEY,
FOCUS_CELL_STORAGE_KEY,
} from '../../constants'

const config = workspace.getConfiguration('runme.checkout')
Expand Down Expand Up @@ -199,17 +200,57 @@ export async function executeActiveNotebookCell({
}
}

export async function setCurrentCellExecutionDemo(context: ExtensionContext, cell: number) {
if (isNaN(cell)) {
export async function focusActiveNotebookCell({ cell, kernel }: { cell: number; kernel: Kernel }) {
const notebookDocument = window.activeNotebookEditor?.notebook
if (!notebookDocument || notebookDocument.notebookType !== Kernel.type) {
return
}
await context.globalState.update(EXECUTION_CELL_STORAGE_KEY, cell)
await context.globalState.update(EXECUTION_CELL_CREATION_DATE_STORAGE_KEY, new Date())
if (notebookDocument) {
const cells = notebookDocument.getCells().filter((cell) => cell.kind === NotebookCellKind.Code)

if (!cells.length || Number.isNaN(cell)) {
return window.showErrorMessage('Could not find a valid code cell to focus')
}

const cellToFocus = cells[cell]
if (!cellToFocus) {
throw new Error(`Could not find cell at index ${cell}`)
}
return kernel.focusNotebookCell(cellToFocus)
}
}

export function shouldExecuteDemo(context: ExtensionContext): boolean {
export async function setCurrentCellForBootFile(
context: ExtensionContext,
{ cell, focus = true, execute = false }: { cell: number; focus: boolean; execute: boolean },
) {
if (!Number.isFinite(cell)) {
return
}
if (focus) {
await context.globalState.update(FOCUS_CELL_STORAGE_KEY, cell)
}
if (execute) {
await context.globalState.update(EXECUTION_CELL_STORAGE_KEY, cell)
}
await context.globalState.update(CELL_CREATION_DATE_STORAGE_KEY, new Date())
}

export function shouldFocusCell(context: ExtensionContext): boolean {
const cell = context.globalState.get<number>(FOCUS_CELL_STORAGE_KEY)
const creationDate = context.globalState.get<string>(CELL_CREATION_DATE_STORAGE_KEY)
if (typeof cell === 'number' && cell >= 0 && creationDate) {
const timeStampDiff =
Math.abs(new Date().getTime() - new Date(creationDate).getTime()) / (1000 * 60)
// Max diff of 5 minutes to focus a cell
return timeStampDiff <= 5
}
return false
}

export function shouldExecuteCell(context: ExtensionContext): boolean {
const cell = context.globalState.get<number>(EXECUTION_CELL_STORAGE_KEY)
const creationDate = context.globalState.get<string>(EXECUTION_CELL_CREATION_DATE_STORAGE_KEY)
const creationDate = context.globalState.get<string>(CELL_CREATION_DATE_STORAGE_KEY)
if (typeof cell === 'number' && cell >= 0 && creationDate) {
const timeStampDiff =
Math.abs(new Date().getTime() - new Date(creationDate).getTime()) / (1000 * 60)
Expand All @@ -219,12 +260,13 @@ export function shouldExecuteDemo(context: ExtensionContext): boolean {
return false
}

export async function cleanExecutionDemo(context: ExtensionContext) {
export async function cleanExecutionForBootFile(context: ExtensionContext) {
await context.globalState.update(FOCUS_CELL_STORAGE_KEY, undefined)
await context.globalState.update(EXECUTION_CELL_STORAGE_KEY, undefined)
await context.globalState.update(EXECUTION_CELL_CREATION_DATE_STORAGE_KEY, undefined)
await context.globalState.update(CELL_CREATION_DATE_STORAGE_KEY, undefined)
}

export function createDemoFileRunnerWatcher(context: ExtensionContext, kernel: Kernel) {
export function createBootFileRunnerWatcher(context: ExtensionContext, kernel: Kernel) {
const fileWatcher = workspace.createFileSystemWatcher(`**/*${BOOTFILE_DEMO}`)
return fileWatcher.onDidCreate(async (uri: Uri) => {
for (let i = 0; i < 50; i++) {
Expand All @@ -246,22 +288,33 @@ export function createDemoFileRunnerWatcher(context: ExtensionContext, kernel: K
kernel,
})
} else {
await setCurrentCellExecutionDemo(context, Number(cell))
await setCurrentCellForBootFile(context, { cell: Number(cell), focus: true, execute: true })
await commands.executeCommand('vscode.openWith', notebookUri, Kernel.type)
}
})
}

export function createDemoFileRunnerForActiveNotebook(context: ExtensionContext, kernel: Kernel) {
export function createBootFileRunnerForActiveNotebook(context: ExtensionContext, kernel: Kernel) {
return window.onDidChangeActiveNotebookEditor(async () => {
if (shouldExecuteDemo(context)) {
const cell = context.globalState.get<number>(EXECUTION_CELL_STORAGE_KEY)
// Remove the execution cell from the storage
await cleanExecutionDemo(context)
await executeActiveNotebookCell({
cell: cell!,
if (shouldFocusCell(context)) {
const focusCell = context.globalState.get<number>(FOCUS_CELL_STORAGE_KEY)
await focusActiveNotebookCell({
cell: focusCell!,
kernel,
})
await cleanExecutionForBootFile(context)
return
}

if (!shouldExecuteCell(context)) {
return
}
const execCell = context.globalState.get<number>(EXECUTION_CELL_STORAGE_KEY)
await executeActiveNotebookCell({
cell: execCell!,
kernel,
})

await cleanExecutionForBootFile(context)
})
}
22 changes: 14 additions & 8 deletions src/extension/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ import getLogger from './logger'
import { Kernel } from './kernel'
import { BOOTFILE, BOOTFILE_DEMO } from './constants'
import { IRunnerEnvironment } from './runner/environment'
import { setCurrentCellExecutionDemo } from './handler/utils'
import { setCurrentCellForBootFile as setCurrentCellForBootFile } from './handler/utils'
import ContextState from './contextState'
import { GCPResolver } from './resolvers/gcpResolver'
import { AWSResolver } from './resolvers/awsResolver'
Expand Down Expand Up @@ -485,27 +485,33 @@ export async function bootFile(context: ExtensionContext) {
}

const startupFileUri = Uri.joinPath(workspace.workspaceFolders[0].uri, BOOTFILE)
const runnableFileUri = Uri.joinPath(workspace.workspaceFolders[0].uri, BOOTFILE_DEMO)
const demoFileUri = Uri.joinPath(workspace.workspaceFolders[0].uri, BOOTFILE_DEMO)
const hasStartupFile = await workspace.fs.stat(startupFileUri).then(
() => true,
() => false,
)

const hasRunnableFile = await workspace.fs.stat(runnableFileUri).then(
const hasDemoFile = await workspace.fs.stat(demoFileUri).then(
() => true,
() => false,
)

const fileUri = hasRunnableFile ? runnableFileUri : startupFileUri
const fileUri = hasDemoFile ? demoFileUri : startupFileUri

if (hasStartupFile || hasRunnableFile) {
if (hasStartupFile || hasDemoFile) {
let bootFile = new TextDecoder().decode(await workspace.fs.readFile(fileUri))
if (hasRunnableFile) {
const [fileName, cell] = bootFile.split('#')
const [fileName, cell] = bootFile.trim().split('#')

if (fileName) {
bootFile = fileName
await setCurrentCellExecutionDemo(context, Number(cell))
}

await setCurrentCellForBootFile(context, {
cell: Number(cell),
focus: true,
execute: hasDemoFile,
})

const bootFileUri = Uri.joinPath(workspace.workspaceFolders[0].uri, bootFile)
await workspace.fs.delete(fileUri)
log.info(`Open file defined in "${BOOTFILE}" file: ${bootFileUri.fsPath}`)
Expand Down
Loading