| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111 |
- import { Terminal } from '@xterm/xterm'
- import { FitAddon } from '@xterm/addon-fit'
- import { WebLinksAddon } from '@xterm/addon-web-links'
- 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()
- const linkHandler = {
- activate (event, text, _range) {
- event.preventDefault()
- window.open(text, '_blank')
- }
- }
- this.terminal = new Terminal({
- convertEol: true,
- linkHandler,
- scrollback: 10000
- })
- const fitAddon = new FitAddon()
- this.terminal.loadAddon(fitAddon)
- this.terminal.fit = fitAddon
- this.terminal.loadAddon(new WebLinksAddon((event, uri) => linkHandler.activate(event, uri)))
- this.linkHandlerConfigured = true
- }
- 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)
- }
- /**
- * Get the terminal buffer content as a string.
- * This method is intended for use in integration tests to verify output.
- * @returns {string} The terminal buffer content as a string
- */
- getBufferAsString () {
- if (!this.terminal) {
- return ''
- }
- const buffer = this.terminal.buffer.active
- let text = ''
- for (let i = 0; i < buffer.length; i++) {
- const line = buffer.getLine(i)
- if (line) {
- text += line.translateToString(true) + '\n'
- }
- }
- return text
- }
- }
|