@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
JavaScript
"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;
}
});
}
}