const MINIMUM_COLS = 2 const MINIMUM_ROWS = 1 class FitAddon { constructor() {} activate(terminal) { this._terminal = terminal } dispose() {} fit() { const dims = this.proposeDimensions() if (!dims || !this._terminal || isNaN(dims.cols) || isNaN(dims.rows)) { return } // TODO: Remove reliance on private API const core = this._terminal._core // Force a full render if ( this._terminal.rows !== dims.rows || this._terminal.cols !== dims.cols ) { core._renderService.clear() this._terminal.resize(dims.cols, dims.rows) } } proposeDimensions() { if (!this._terminal) { return undefined } if (!this._terminal.element || !this._terminal.element.parentElement) { return undefined } // TODO: Remove reliance on private API const core = this._terminal._core const dims = core._renderService.dimensions if (dims.actualCellWidth === 0 || dims.actualCellHeight === 0) { return undefined } const scrollbarWidth = this._terminal.options.scrollback === 0 ? 0 : core.viewport.scrollBarWidth const parentElementStyle = window.getComputedStyle( this._terminal.element.parentElement ) const parentElementHeight = parseInt( parentElementStyle.getPropertyValue("height") ) const parentElementWidth = Math.max( 0, parseInt(parentElementStyle.getPropertyValue("width")) ) const elementStyle = window.getComputedStyle(this._terminal.element) const elementPadding = { top: parseInt(elementStyle.getPropertyValue("padding-top")), bottom: parseInt(elementStyle.getPropertyValue("padding-bottom")), right: parseInt(elementStyle.getPropertyValue("padding-right")), left: parseInt(elementStyle.getPropertyValue("padding-left")) } const elementPaddingVer = elementPadding.top + elementPadding.bottom const elementPaddingHor = elementPadding.right + elementPadding.left const availableHeight = parentElementHeight - elementPaddingVer const availableWidth = parentElementWidth - elementPaddingHor - scrollbarWidth const geometry = { cols: Math.max( MINIMUM_COLS, Math.floor(availableWidth / dims.actualCellWidth) ), rows: Math.max( MINIMUM_ROWS, Math.floor(availableHeight / dims.actualCellHeight) ) } return geometry } } const term = new Terminal( { cursorBlink: true, fontSize: 16, theme: { background: '#282C34', foreground: '#ffffff', cursor: '#ffffff', cursorAccent: '#282C34', selection: '#41454E', }, } ); var command = ""; var need_more_lines = false; var stopped = false; var repl = 0; var Module = { 'print': function(text) { term.write(text + "\r\n"); }, 'printErr': function(text) { term.write(text + "\r\n"); }, 'onRuntimeInitialized': function(text) { var vm = Module.ccall('pkpy_new_vm', 'number', ['boolean'], [true]); repl = Module.ccall('pkpy_new_repl', 'number', ['number'], [vm]); term.write(need_more_lines ? "... " : ">>> "); }, 'onAbort': function(text) { stopped = true; }, }; function term_init() { term.open(document.getElementById('terminal')); const addon = new FitAddon(); term.loadAddon(addon); addon.fit(); // refit when window is resized window.addEventListener('resize', () => { addon.fit(); }); term.onData(e => { if (stopped) return; switch (e) { case '\r': // Enter term.write("\r\n"); need_more_lines = Module.ccall('pkpy_repl_input', 'bool', ['number', 'string'], [repl, command]); command = ''; term.write(need_more_lines ? "... " : ">>> "); break; case '\u007F': // Backspace (DEL) // Do not delete the prompt if (term._core.buffer.x > 4) { // '>>> ' or '... ' var re=/[^\u4E00-\u9FA5]/; if (re.test(command.charAt(command.length-1))){//判断前一个字符是否为中文 term.write('\b \b');//false }else{ term.write('\b\b \b');//中文占两个字节,ascii字符占一个字节 } if (command.length > 0) { command = command.substr(0, command.length - 1); } } break; default: // Print all other characters for demo if (e >= String.fromCharCode(0x20) && e <= String.fromCharCode(0x7E) || e >= '\u00a0') { command += e; term.write(e); } } }); }