UNPKG

@vscode/sync-api-service

Version:

A sync implementation of the VS Code API. Only supported from a worker in NodeJS and browser

357 lines (356 loc) 11.7 kB
"use strict"; /* -------------------------------------------------------------------------------------------- * Copyright (c) Microsoft Corporation. All rights reserved. * Licensed under the MIT License. See License.txt in the project root for license information. * ------------------------------------------------------------------------------------------ */ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { if (k2 === undefined) k2 = k; var desc = Object.getOwnPropertyDescriptor(m, k); if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { desc = { enumerable: true, get: function() { return m[k]; } }; } Object.defineProperty(o, k2, desc); }) : (function(o, m, k, k2) { if (k2 === undefined) k2 = k; o[k2] = m[k]; })); var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { Object.defineProperty(o, "default", { enumerable: true, value: v }); }) : function(o, v) { o["default"] = v; }); var __importStar = (this && this.__importStar) || function (mod) { if (mod && mod.__esModule) return mod; var result = {}; if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); __setModuleDefault(result, mod); return result; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.ServicePseudoTerminal = exports.TerminalMode = void 0; const vscode_1 = require("vscode"); const uuid = __importStar(require("uuid")); const sync_api_common_1 = require("@vscode/sync-api-common"); class LineBuffer { constructor() { this.cursor = 0; this.content = []; } clear() { this.cursor = 0; this.content = []; } getLine() { return this.content.join(''); } getCursor() { return this.cursor; } isCursorAtEnd() { return this.cursor === this.content.length; } isCursorAtBeginning() { return this.cursor === 0; } insert(value) { for (const char of value) { this.content.splice(this.cursor, 0, char); this.cursor++; } } del() { if (this.cursor === this.content.length) { return false; } this.content.splice(this.cursor, 1); return true; } backspace() { if (this.cursor === 0) { return false; } this.cursor -= 1; this.content.splice(this.cursor, 1); return true; } moveCursorRelative(characters) { const newValue = this.cursor + characters; if (newValue < 0 || newValue > this.content.length) { return false; } this.cursor = newValue; return true; } moveCursorStartOfLine() { if (this.cursor === 0) { return false; } this.cursor = 0; return true; } moveCursorEndOfLine() { if (this.cursor === this.content.length) { return false; } this.cursor = this.content.length; return true; } moveCursorWordLeft() { if (this.cursor === 0) { return false; } let index; // check if we are at the beginning of a word if (this.content[this.cursor - 1] === ' ') { index = this.cursor - 2; while (index > 0) { if (this.content[index] === ' ') { index--; } else { break; } } } else { index = this.cursor; } if (index === 0) { this.cursor = index; return true; } // On the first character that is not space while (index > 0) { if (this.content[index] === ' ') { index++; break; } else { index--; } } this.cursor = index; return true; } moveCursorWordRight() { if (this.cursor === this.content.length) { return false; } let index; if (this.content[this.cursor] === ' ') { index = this.cursor + 1; while (index < this.content.length) { if (this.content[index] === ' ') { index++; } else { break; } } } else { index = this.cursor; } if (index === this.content.length) { this.cursor = index; return true; } while (index < this.content.length) { if (this.content[index] === ' ') { break; } else { index++; } } this.cursor = index; return true; } } var TerminalMode; (function (TerminalMode) { TerminalMode[TerminalMode["idle"] = 1] = "idle"; TerminalMode[TerminalMode["inUse"] = 2] = "inUse"; })(TerminalMode = exports.TerminalMode || (exports.TerminalMode = {})); var ServicePseudoTerminal; (function (ServicePseudoTerminal) { function create() { return new ServiceTerminalImpl(); } ServicePseudoTerminal.create = create; })(ServicePseudoTerminal = exports.ServicePseudoTerminal || (exports.ServicePseudoTerminal = {})); const terminalRegExp = /(\r\n)|(\n)/gm; class ServiceTerminalImpl { constructor() { this.mode = TerminalMode.inUse; this._onDidClose = new vscode_1.EventEmitter(); this.onDidClose = this._onDidClose.event; this._onDidWrite = new vscode_1.EventEmitter(); this.onDidWrite = this._onDidWrite.event; this._onDidChangeName = new vscode_1.EventEmitter; this.onDidChangeName = this._onDidChangeName.event; this._onDidCtrlC = new vscode_1.EventEmitter; this.onDidCtrlC = this._onDidCtrlC.event; this._onAnyKey = new vscode_1.EventEmitter; this.onAnyKey = this._onAnyKey.event; const id = this.id = uuid.v4(); this.encoder = (0, sync_api_common_1.RAL)().TextEncoder.create(); this.decoder = (0, sync_api_common_1.RAL)().TextDecoder.create(); this.uri = vscode_1.Uri.from({ scheme: 'terminal', authority: id }); this.fileDescriptor = { kind: 'terminal', uri: this.uri }; this.lines = []; this.lineBuffer = new LineBuffer(); this.isOpen = false; } setMode(mode) { this.mode = mode; } setName(name) { if (this.isOpen) { this._onDidChangeName.fire(name); } else { this.nameBuffer = name; } } open() { this.isOpen = true; if (this.nameBuffer !== undefined) { this._onDidChangeName.fire(this.nameBuffer); this.nameBuffer = undefined; } if (this.writeBuffer !== undefined) { for (const item of this.writeBuffer) { this._onDidWrite.fire(item); } this.writeBuffer = undefined; } } close() { this._onDidClose.fire(); } async read(_maxBytesToRead) { const value = await this.readline(); return this.encoder.encode(value); } readline() { if (this.readlineCallback !== undefined) { throw new Error(`Already in readline mode`); } if (this.lines.length > 0) { return Promise.resolve(this.lines.shift()); } return new Promise((resolve) => { this.readlineCallback = resolve; }); } write(bytes) { this.writeString(this.getString(bytes)); return Promise.resolve(bytes.byteLength); } writeString(str) { if (this.isOpen) { this._onDidWrite.fire(str); } else { if (this.writeBuffer === undefined) { this.writeBuffer = []; } this.writeBuffer.push(str); } } handleInput(data) { if (this.mode === TerminalMode.idle) { this._onAnyKey.fire(); return; } const previousCursor = this.lineBuffer.getCursor(); switch (data) { case '\x03': // ctrl+C this._onDidCtrlC.fire(); break; case '\x06': // ctrl+f case '\x1b[C': // right this.adjustCursor(this.lineBuffer.moveCursorRelative(1), previousCursor, this.lineBuffer.getCursor()); break; case '\x1bf': // alt+f case '\x1b[1;5C': // ctrl+right this.adjustCursor(this.lineBuffer.moveCursorWordRight(), previousCursor, this.lineBuffer.getCursor()); break; case '\x02': // ctrl+b case '\x1b[D': // left this.adjustCursor(this.lineBuffer.moveCursorRelative(-1), previousCursor, this.lineBuffer.getCursor()); break; case '\x1bb': // alt+b case '\x1b[1;5D': // ctrl+left this.adjustCursor(this.lineBuffer.moveCursorWordLeft(), previousCursor, this.lineBuffer.getCursor()); break; case '\x01': // ctrl+a case '\x1b[H': // home this.adjustCursor(this.lineBuffer.moveCursorStartOfLine(), previousCursor, this.lineBuffer.getCursor()); break; case '\x05': // ctrl+e case '\x1b[F': // end this.adjustCursor(this.lineBuffer.moveCursorEndOfLine(), previousCursor, this.lineBuffer.getCursor()); break; case '\x1b[A': // up this.bell(); break; case '\x1b[B': // down this.bell(); break; case '\x08': // shift+backspace case '\x7F': // backspace this.lineBuffer.backspace() ? this._onDidWrite.fire('\x1b[D\x1b[P') : this.bell(); break; case '\x1b[3~': // delete key this.lineBuffer.del() ? this._onDidWrite.fire('\x1b[P') : this.bell(); break; case '\r': // enter this.handleEnter(); break; default: this.lineBuffer.insert(data); if (!this.lineBuffer.isCursorAtEnd()) { this._onDidWrite.fire('\x1b[@'); } this._onDidWrite.fire(data); } } handleEnter() { this._onDidWrite.fire('\r\n'); const line = this.lineBuffer.getLine(); this.lineBuffer.clear(); this.lines.push(line); if (this.readlineCallback !== undefined) { this.readlineCallback(`${this.lines.shift()}\n`); this.readlineCallback = undefined; } } adjustCursor(success, oldCursor, newCursor) { if (!success) { this.bell(); return; } const change = oldCursor - newCursor; const code = change > 0 ? 'D' : 'C'; const sequence = `\x1b[${code}`.repeat(Math.abs(change)); this._onDidWrite.fire(sequence); } bell() { this._onDidWrite.fire('\x07'); } getString(bytes) { return this.decoder.decode(bytes.slice()).replace(terminalRegExp, (match, m1, m2) => { if (m1) { return m1; } else if (m2) { return '\r\n'; } else { return match; } }); } }