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