Files
xterm.js/bin/extract_vtfeatures.js
T

330 lines
8.5 KiB
JavaScript

/**
* Copyright (c) 2019 The xterm.js authors. All rights reserved.
* @license MIT
*
* Script to extract vt features documented in docstrings.
*/
const fs = require('fs');
const Mustache = require('mustache');
/**
* regexp to fetch all comments
* Fetches all multiline comments and single lines containing '// @vt:'.
*/
const REX_COMMENTS = /^\s*?[/][*][*]([\s\S]*?)[*][/]|^\s*?\/\/ ([@]vt[:].*?)$/mug;
/**
* regexp to parse the @vt line
* expected data - "@vt: <status> <kind> <mnemonic> "<name>" "<sequence>" "<short description>"
*/
const REX_VT_LINE = /^[@]vt\:\s*(\w+)\s*(\w+)\s*(\w+)\s*"(.*?)"\s*"(.*?)"\s*"(.*?)".*$/;
// known vt command types
const TYPES = [
'C0',
'C1',
'ESC',
'CSI',
'DCS',
'OSC',
'APC',
'PM',
'SOS'
];
const MARKDOWN_TMPL = `
# Supported VT features by xterm.js
Version: {{version}}
### Table of Contents
- [General notes](#general-notes)
{{#C0.length}}
- [C0](#c0)
{{/C0.length}}
{{#C1.length}}
- [C1](#c1)
{{/C1.length}}
{{#CSI.length}}
- [CSI](#csi)
{{/CSI.length}}
{{#DCS.length}}
- [DCS](#dcs)
{{/DCS.length}}
{{#ESC.length}}
- [ESC](#esc)
{{/ESC.length}}
{{#OSC.length}}
- [OSC](#osc)
{{/OSC.length}}
### General notes
This document lists xterm.js' support of typical VT commands. The commands are grouped by their type:
- C0: single byte command (7bit control characters, byte range \\x00 .. \\x1f)
- C1: single byte command (8bit control characters, byte range \\x80 .. \\x9f)
- ESC: sequence starting with \`ESC\` (\`\\x1b\`)
- CSI - Control Sequence Introducer: sequence starting with \`ESC [\` (7bit) or CSI (\`\\x9b\` 8bit)
- DCS - Device Control String: sequence starting with \`ESC P\` (7bit) or DCS (\`\\x90\` 8bit)
- OSC - Operating System Command: sequence starting with \`ESC ]\` (7bit) or OSC (\`\\x9d\` 8bit)
Application Program Command (APC), Privacy Message (PM) and Start of String (SOS) are not supported,
any sequence of these types will be ignored.
Note that the list only contains commands implemented in xterm.js' core codebase. Missing commands are either
not supported or unstable/experimental. Furthermore addons can provide additional commands.
To denote the sequences the lists use the same abbreviations as xterm does:
- \`Ps\`: A single (usually optional) numeric parameter, composed of one or more decimal digits.
- \`Pm\`: A multiple numeric parameter composed of any number of single numeric parameters, separated by ; character(s),
e.g. \` Ps ; Ps ; ... \`.
- \`Pt\`: A text parameter composed of printable characters. Note that for most commands with \`Pt\` only
ASCII printables are specified to work. Additionally xterm.js will let any character >C1 pass as printable.
{{#C0.length}}
### C0
| Mnemonic | Name | Sequence | Short Description | Status |
| -------- | ---- | -------- | ----------------- | ------ |
{{#C0}}
| {{mnemonic}} | {{name}} | \`{{sequence}}\` | {{{shortDescription}}} {{#longDescription.length}}_[more](#{{longTarget}})_{{/longDescription.length}} | {{status}} |
{{/C0}}
{{#C0.hasLongDescriptions}}
{{#C0}}
{{#longDescription.length}}
#### {{name}}
{{#longDescription}}
{{{.}}}
{{/longDescription}}
{{/longDescription.length}}
{{/C0}}
{{/C0.hasLongDescriptions}}
{{/C0.length}}
{{#C1.length}}
### C1
| Mnemonic | Name | Sequence | Short Description | Status |
| -------- | ---- | -------- | ----------------- | ------ |
{{#C1}}
| {{mnemonic}} | {{name}} | \`{{sequence}}\` | {{{shortDescription}}} {{#longDescription.length}}_[more](#{{longTarget}})_{{/longDescription.length}} | {{status}} |
{{/C1}}
{{#C1.hasLongDescriptions}}
{{#C1}}
{{#longDescription.length}}
#### {{name}}
{{#longDescription}}
{{{.}}}
{{/longDescription}}
{{/longDescription.length}}
{{/C1}}
{{/C1.hasLongDescriptions}}
{{/C1.length}}
{{#CSI.length}}
### CSI
| Mnemonic | Name | Sequence | Short Description | Status |
| -------- | ---- | -------- | ----------------- | ------ |
{{#CSI}}
| {{mnemonic}} | {{name}} | \`{{sequence}}\` | {{{shortDescription}}} {{#longDescription.length}}_[more](#{{longTarget}})_{{/longDescription.length}} | {{status}} |
{{/CSI}}
{{#CSI.hasLongDescriptions}}
{{#CSI}}
{{#longDescription.length}}
#### {{name}}
{{#longDescription}}
{{{.}}}
{{/longDescription}}
{{/longDescription.length}}
{{/CSI}}
{{/CSI.hasLongDescriptions}}
{{/CSI.length}}
{{#DCS.length}}
### DCS
| Mnemonic | Name | Sequence | Short Description | Status |
| -------- | ---- | -------- | ----------------- | ------ |
{{#DCS}}
| {{mnemonic}} | {{name}} | \`{{sequence}}\` | {{{shortDescription}}} {{#longDescription.length}}_[more](#{{longTarget}})_{{/longDescription.length}} | {{status}} |
{{/DCS}}
{{#DCS.hasLongDescriptions}}
{{#DCS}}
{{#longDescription.length}}
#### {{name}}
{{#longDescription}}
{{{.}}}
{{/longDescription}}
{{/longDescription.length}}
{{/DCS}}
{{/DCS.hasLongDescriptions}}
{{/DCS.length}}
{{#ESC.length}}
### ESC
| Mnemonic | Name | Sequence | Short Description | Status |
| -------- | ---- | -------- | ----------------- | ------ |
{{#ESC}}
| {{mnemonic}} | {{name}} | \`{{sequence}}\` | {{{shortDescription}}} {{#longDescription.length}}_[more](#{{longTarget}})_{{/longDescription.length}} | {{status}} |
{{/ESC}}
{{#ESC.hasLongDescriptions}}
{{#ESC}}
{{#longDescription.length}}
#### {{name}}
{{#longDescription}}
{{{.}}}
{{/longDescription}}
{{/longDescription.length}}
{{/ESC}}
{{/ESC.hasLongDescriptions}}
{{/ESC.length}}
{{#OSC.length}}
### OSC
| Identifier | Sequence | Short Description | Status |
| ---------- | -------- | ----------------- | ------ |
{{#OSC}}
| {{mnemonic}} | \`{{sequence}}\` | {{{shortDescription}}} {{#longDescription.length}}_[more](#{{longTarget}})_{{/longDescription.length}} | {{status}} |
{{/OSC}}
{{#OSC.hasLongDescriptions}}
{{#OSC}}
{{#longDescription.length}}
#### {{name}}
{{#longDescription}}
{{{.}}}
{{/longDescription}}
{{/longDescription.length}}
{{/OSC}}
{{/OSC.hasLongDescriptions}}
{{/OSC.length}}
### TODO
- improve table sorting:
- sort C0/C1 in byte order
- sort OSC in numerical order
- sort CSI/ESC/DCS in final byte order
- references
`
function createAnchorSlug(s) {
return s.toLowerCase().split(' ').join('-');
}
function parseMultiLine(filename, s) {
if (!~s.indexOf('@vt:')) {
return;
}
const lines = s.split('\n').map(el => el.trim().replace(/[*]/, '').replace(/\s/, ''));
let grabLine = false;
const longDescription = [];
let feature;
for (const line of lines) {
if (grabLine) {
if (!line) {
break;
}
longDescription.push(line);
}
if (~line.indexOf('@vt:')) {
feature = parseSingleLine(filename, line);
grabLine = true;
}
}
if (feature) {
feature.longDescription = longDescription;
feature.longTarget = createAnchorSlug(feature.name);
return feature;
}
}
function parseSingleLine(filename, s) {
const line = s.trim();
const match = line.match(REX_VT_LINE);
if (match !== null) {
if (!~TYPES.indexOf(match[2])) {
throw new Error(`unkown vt-command type "${match[2]}" specified in "${filename}"`);
}
return {
status: match[1],
type: match[2],
mnemonic: match[3],
name: match[4],
sequence: match[5],
shortDescription: match[6],
longDescription: [],
longTarget: '',
source: filename
};
}
}
function postProcessData(features) {
const featureTable = {};
for (const feature of features) {
if (featureTable[feature.type] === undefined) {
featureTable[feature.type] = [];
}
featureTable[feature.type].push(feature);
if (feature.longDescription) {
featureTable[feature.type].hasLongDescriptions = true;
}
}
for (const entry in featureTable) {
featureTable[entry].sort((a, b) => a.sequence.slice(-1) > b.sequence.slice(-1));
}
// console.error(featureTable);
featureTable.version = require('../package.json').version;
console.log(Mustache.render(MARKDOWN_TMPL, featureTable));
}
function main(filenames) {
// console.error(filenames);
let leftToProcess = filenames.length;
const features = [];
for (const filename of filenames) {
fs.readFile(filename, 'utf-8', (err, data) => {
let match;
while ((match = REX_COMMENTS.exec(data)) !== null) {
if (match.index === REX_COMMENTS.lastIndex) {
REX_COMMENTS.lastIndex++;
}
const feature = match[1]
? parseMultiLine(filename, match[1])
: parseSingleLine(filename, match[2]);
if (feature) {
features.push(feature);
}
}
leftToProcess--;
if (!leftToProcess) {
postProcessData(features);
}
});
}
}
main(process.argv.slice(2))