parent
b91c0d7e45
commit
af75afa82f
|
|
@ -28,24 +28,20 @@ export class ExecutionDialog {
|
|||
|
||||
window.terminal = new OutputTerminal()
|
||||
window.terminal.open(this.domOutput)
|
||||
}
|
||||
|
||||
showOutput () {
|
||||
this.domOutput.hidden = false
|
||||
this.domOutput.hidden = false
|
||||
window.terminal.resize(80, 24)
|
||||
}
|
||||
|
||||
toggleSize () {
|
||||
if (this.dlg.classList.contains('big')) {
|
||||
this.dlg.classList.remove('big')
|
||||
window.terminal.resize(80, 24)
|
||||
} else {
|
||||
this.dlg.classList.add('big')
|
||||
}
|
||||
|
||||
window.terminal.fit()
|
||||
}
|
||||
}
|
||||
|
||||
reset () {
|
||||
async reset () {
|
||||
this.executionSeconds = 0
|
||||
this.executionTrackingId = 'notset'
|
||||
|
||||
|
|
@ -69,7 +65,7 @@ export class ExecutionDialog {
|
|||
|
||||
this.domExecutionDetails.hidden = true
|
||||
|
||||
window.terminal.reset()
|
||||
await window.terminal.reset()
|
||||
window.terminal.fit()
|
||||
}
|
||||
|
||||
|
|
@ -193,7 +189,7 @@ export class ExecutionDialog {
|
|||
}
|
||||
}
|
||||
|
||||
renderExecutionResult (res) {
|
||||
async renderExecutionResult (res) {
|
||||
this.res = res
|
||||
|
||||
clearInterval(window.executionDialogTicker)
|
||||
|
|
@ -229,8 +225,8 @@ export class ExecutionDialog {
|
|||
|
||||
this.updateDuration(res.logEntry)
|
||||
|
||||
window.terminal.reset()
|
||||
window.terminal.write(res.logEntry.output, () => {
|
||||
await window.terminal.reset()
|
||||
await window.terminal.write(res.logEntry.output, () => {
|
||||
window.terminal.fit()
|
||||
})
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,24 @@
|
|||
export class Mutex {
|
||||
constructor() {
|
||||
this._locked = false;
|
||||
this._waiting = [];
|
||||
}
|
||||
|
||||
lock() {
|
||||
const unlock = () => {
|
||||
const next = this._waiting.shift();
|
||||
if (next) {
|
||||
next(unlock);
|
||||
} else {
|
||||
this._locked = false;
|
||||
}
|
||||
};
|
||||
|
||||
if (this._locked) {
|
||||
return new Promise(resolve => this._waiting.push(resolve)).then(() => unlock);
|
||||
} else {
|
||||
this._locked = true;
|
||||
return Promise.resolve(unlock);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,8 +1,22 @@
|
|||
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 () {
|
||||
this.writeMutex = new Mutex()
|
||||
this.terminal = new Terminal({
|
||||
convertEol: true
|
||||
})
|
||||
|
|
@ -12,8 +26,33 @@ export class OutputTerminal {
|
|||
this.terminal.fit = fitAddon
|
||||
}
|
||||
|
||||
write (out, then) {
|
||||
this.terminal.write(out, then)
|
||||
async write (out, then) {
|
||||
const unlock = await this.writeMutex.lock()
|
||||
|
||||
try {
|
||||
await new Promise(resolve => {
|
||||
this.terminal.write(out, () => {
|
||||
resolve()
|
||||
})
|
||||
})
|
||||
} finally {
|
||||
unlock()
|
||||
then()
|
||||
}
|
||||
}
|
||||
|
||||
async reset () {
|
||||
const unlock = await this.writeMutex.lock()
|
||||
|
||||
try {
|
||||
await new Promise(resolve => {
|
||||
this.terminal.clear()
|
||||
this.terminal.reset()
|
||||
resolve()
|
||||
})
|
||||
} finally {
|
||||
unlock()
|
||||
}
|
||||
}
|
||||
|
||||
fit () {
|
||||
|
|
@ -24,7 +63,7 @@ export class OutputTerminal {
|
|||
this.terminal.open(el)
|
||||
}
|
||||
|
||||
reset () {
|
||||
this.terminal.reset()
|
||||
resize (cols, rows) {
|
||||
this.terminal.resize(cols, rows)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -170,8 +170,6 @@ function onOutputChunk (evt) {
|
|||
|
||||
if (chunk.executionTrackingId === window.executionDialog.executionTrackingId) {
|
||||
window.terminal.write(chunk.output)
|
||||
|
||||
window.executionDialog.showOutput()
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue