Skip to content

Commit

Permalink
better error message when go fails
Browse files Browse the repository at this point in the history
  • Loading branch information
christian-bromann committed Oct 4, 2022
1 parent ba02ed7 commit 901d8d4
Show file tree
Hide file tree
Showing 6 changed files with 147 additions and 74 deletions.
2 changes: 1 addition & 1 deletion src/extension/executors/index.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { sh, bash } from './shell'
import { sh, bash } from './task'
import { vercel } from './vercel'
import { html } from './html'

Expand Down
107 changes: 51 additions & 56 deletions src/extension/executors/shell.ts
Original file line number Diff line number Diff line change
@@ -1,81 +1,76 @@
import path from 'node:path'
import { writeFile, chmod } from 'node:fs/promises'
import { spawn } from 'node:child_process'
import type EventEmitter from 'node:events'

import {
Task, ShellExecution, TextDocument, NotebookCellExecution, TaskScope, tasks,
window, TerminalOptions, commands, ExtensionContext
TextDocument, NotebookCellOutput, NotebookCellOutputItem, NotebookCellExecution,
ExtensionContext
} from 'vscode'
import { file } from 'tmp-promise'

const LABEL_LIMIT = 15

export function closeTerminalByScript (script: string) {
const terminal = window.terminals.find((t) => (
t.creationOptions as TerminalOptions).shellArgs?.includes(script))
if (terminal) {
terminal.hide()
}
}
import { OutputType } from '../../constants'
import type { CellOutput } from '../../types'

async function shellExecutor(
context: ExtensionContext,
exec: NotebookCellExecution,
doc: TextDocument,
inputHandler: EventEmitter
): Promise<boolean> {
const outputItems: string[] = []
const scriptFile = await file()
const cellText = doc.getText()
await writeFile(scriptFile.path, cellText, 'utf-8')
await writeFile(scriptFile.path, doc.getText(), 'utf-8')
await chmod(scriptFile.path, 0o775)

const shellExecution = new Task(
{ type: 'runme', name: 'Runme Task' },
TaskScope.Workspace,
cellText.length > LABEL_LIMIT
? `${cellText.slice(0, LABEL_LIMIT)}...`
: cellText,
'exec',
new ShellExecution(scriptFile.path, {
cwd: path.dirname(doc.uri.path)
})
)
await commands.executeCommand('workbench.action.terminal.clear')
const execution = await tasks.executeTask(shellExecution)
const child = spawn(scriptFile.path, {
cwd: path.dirname(doc.uri.path),
shell: true
})
console.log(`[RunMe] Started process on pid ${child.pid}`)

inputHandler.on('data', (input) => {
child.stdin.write(`${input}\n`)
/**
* the following prevents a second prompt to accept any input
* but not calling it causes the process to never exit
*/
// child.stdin.end()
})

/**
* handle output for stdout and stderr
*/
function handleOutput(data: any) {
outputItems.push(data.toString().trim())
exec.replaceOutput(new NotebookCellOutput([
NotebookCellOutputItem.json(<CellOutput>{
type: OutputType.shell,
output: outputItems.join('\n')
}, OutputType.shell)
]))
}

child.stdout.on('data', handleOutput)
child.stderr.on('data', handleOutput)
return !Boolean(await new Promise<number>((resolve) => {
/**
* register cancellation handler
* ToDo(Christian): maybe better to kill with SIGINT signal but that doesn't stop the
* prcoess afterall
*/
exec.token.onCancellationRequested(() => {
try {
execution.terminate()
closeTerminalByScript(scriptFile.path)
resolve(0)
} catch (err: any) {
console.error(`[Runme] Failed to terminate task: ${(err as Error).message}`)
resolve(1)
}
})
child.stdin.destroy()
child.stdout.off('data', handleOutput)
child.stderr.off('data', handleOutput)

tasks.onDidEndTaskProcess((e) => {
const taskId = (e.execution as any)['_id']
const executionId = (execution as any)['_id']

/**
* ignore if
*/
if (
/**
* VS Code is running a different task
*/
taskId !== executionId ||
/**
* we don't have an exit code
*/
typeof e.exitCode === 'undefined'
) {
return
if (child.pid) {
process.kill(child.pid, 'SIGHUP')
}

closeTerminalByScript(scriptFile.path)
return resolve(e.exitCode)
resolve(child.kill('SIGHUP') ? 0 : 1)
})

child.on('exit', resolve)
}))
}

Expand Down
83 changes: 83 additions & 0 deletions src/extension/executors/task.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
import path from 'node:path'
import { writeFile, chmod } from 'node:fs/promises'

import {
Task, ShellExecution, TextDocument, NotebookCellExecution, TaskScope, tasks,
window, TerminalOptions, commands, ExtensionContext
} from 'vscode'
import { file } from 'tmp-promise'

const LABEL_LIMIT = 15

export function closeTerminalByScript (script: string) {
const terminal = window.terminals.find((t) => (
t.creationOptions as TerminalOptions).shellArgs?.includes(script))
if (terminal) {
terminal.hide()
}
}

async function taskExecutor(
context: ExtensionContext,
exec: NotebookCellExecution,
doc: TextDocument,
): Promise<boolean> {
const scriptFile = await file()
const cellText = doc.getText()
await writeFile(scriptFile.path, cellText, 'utf-8')
await chmod(scriptFile.path, 0o775)

const taskExecution = new Task(
{ type: 'runme', name: 'Runme Task' },
TaskScope.Workspace,
cellText.length > LABEL_LIMIT
? `${cellText.slice(0, LABEL_LIMIT)}...`
: cellText,
'exec',
new ShellExecution(scriptFile.path, {
cwd: path.dirname(doc.uri.path)
})
)
await commands.executeCommand('workbench.action.terminal.clear')
const execution = await tasks.executeTask(taskExecution)

return !Boolean(await new Promise<number>((resolve) => {
exec.token.onCancellationRequested(() => {
try {
execution.terminate()
closeTerminalByScript(scriptFile.path)
resolve(0)
} catch (err: any) {
console.error(`[Runme] Failed to terminate task: ${(err as Error).message}`)
resolve(1)
}
})

tasks.onDidEndTaskProcess((e) => {
const taskId = (e.execution as any)['_id']
const executionId = (execution as any)['_id']

/**
* ignore if
*/
if (
/**
* VS Code is running a different task
*/
taskId !== executionId ||
/**
* we don't have an exit code
*/
typeof e.exitCode === 'undefined'
) {
return
}

closeTerminalByScript(scriptFile.path)
return resolve(e.exitCode)
})
}))
}

export const sh = taskExecutor
export const bash = taskExecutor
2 changes: 1 addition & 1 deletion src/extension/executors/vercel.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import {

import { OutputType } from '../../constants'
import type { CellOutput } from '../../types'
import { bash } from './shell'
import { bash } from './task'
import { deploy, login, logout } from './vercel/index'

export async function vercel (
Expand Down
13 changes: 2 additions & 11 deletions src/extension/extension.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ import vscode from 'vscode'
import { Serializer } from './notebook'
import { Kernel } from './kernel'
import { ViteServerProcess } from './server'
import { ThumbsDownProvider, ThumbsUpProvider } from './provider/rating'

const viteProcess = new ViteServerProcess()

Expand All @@ -14,21 +13,13 @@ export async function activate (context: vscode.ExtensionContext) {
context.subscriptions.push(
kernel,
viteProcess,
vscode.workspace.registerNotebookSerializer("runme", new Serializer(context), {
vscode.workspace.registerNotebookSerializer('runme', new Serializer(context), {
transientOutputs: true,
transientCellMetadata: {
inputCollapsed: true,
outputCollapsed: true,
},
}),
vscode.notebooks.registerNotebookCellStatusBarItemProvider(
"runme",
new ThumbsUpProvider()
),
vscode.notebooks.registerNotebookCellStatusBarItemProvider(
"runme",
new ThumbsDownProvider()
)
})
)

console.log('[Runme] Extension successfully activated')
Expand Down
14 changes: 9 additions & 5 deletions src/extension/notebook.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ const CODE_REGEX = /```(\w+)?\n[^`]*```/g

export class Serializer implements vscode.NotebookSerializer {
private fileContent?: string
private readonly ready: Promise<void>
private readonly ready: Promise<Error | void>

constructor(private context: vscode.ExtensionContext) {
const go = new globalThis.Go()
Expand All @@ -19,21 +19,25 @@ export class Serializer implements vscode.NotebookSerializer {
go.importObject
).then(
(result) => { go.run(result.instance) },
(err: Error) => console.error(err)
(err: Error) => {
console.error(err)
return err
}
)
}

public async deserializeNotebook(content: Uint8Array): Promise<vscode.NotebookData> {
await this.ready
const err = await this.ready

const md = content.toString()
const doc = globalThis.GetDocument(md)

if (!doc) {
if (!doc || err) {
return new vscode.NotebookData([
new vscode.NotebookCellData(
vscode.NotebookCellKind.Markup,
'⚠️ __Error__: document could not be loaded',
'⚠️ __Error__: document could not be loaded' +
(err ? `\n<small>${err.message}</small>` : ''),
'markdown'
)
])
Expand Down

0 comments on commit 901d8d4

Please sign in to comment.