Files
OliveTin/frontend/js/OutputTerminal.js

78 lines
1.7 KiB
JavaScript

import { Terminal } from '@xterm/xterm'
import { FitAddon } from '@xterm/addon-fit'
import { Mutex } from './Mutex.js'
/**
* xterm.js based terminal output for the execution dialog.
*
* the xterm.js methods for write(), reset() and clear() appear to be async,
* but they do not return a Promise and instead use a callback. When calling
* these methods in quick succession, the output can get garbled due to race
* conditions.
*
* To avoid this, this class uses Mutex around those methods to ensure that
* only one write OR reset is executed at a time, is completed, and the calls
* occour in sequential order.
*/
export class OutputTerminal {
constructor (executionTrackingId) {
this.executionTrackingId = executionTrackingId
this.writeMutex = new Mutex()
this.terminal = new Terminal({
convertEol: true
})
const fitAddon = new FitAddon()
this.terminal.loadAddon(fitAddon)
this.terminal.fit = fitAddon
}
async write (out, then) {
const unlock = await this.writeMutex.lock()
try {
await new Promise(resolve => {
this.terminal.write(out, () => {
resolve()
})
})
} finally {
unlock()
if (then != null && then !== undefined) {
then()
}
}
}
async reset () {
const unlock = await this.writeMutex.lock()
try {
await new Promise(resolve => {
this.terminal.clear()
this.terminal.reset()
resolve()
})
} finally {
unlock()
}
}
fit () {
this.terminal.fit.fit()
}
open (el) {
this.terminal.open(el)
}
close () {
this.terminal.dispose()
}
resize (cols, rows) {
this.terminal.resize(cols, rows)
}
}