docutils-ts
Version:
Port of the Python Docutils library to TypeScript
207 lines (206 loc) • 9.06 kB
JavaScript
import RSTState from './rstState.js';
import { columnWidth, isIterable } from '../../../utils.js';
import unescape from '../../../utils/unescape.js';
import * as nodes from '../../../nodes.js';
import * as RegExps from '../regExps.js';
import TransitionCorrection from '../../../transitionCorrection.js';
import UnexpectedIndentationError from '../../../error/unexpectedIndentationError.js';
import { EOFError } from '../../../exceptions.js';
import NestedStateMachine from "../nestedStateMachine.js";
class Text extends RSTState {
constructor() {
super(...arguments);
this.initialTransitions = [['underline', 'Body'], ['text', 'Body']];
this.patterns = {
underline: new RegExp('([!-/:-@[-`{-~])\\1* *$'),
text: new RegExp(''),
};
}
blank(match, context, nextState) {
const [paragraph, literalNext] = this.paragraph(context, this.rstStateMachine.absLineNumber() - 1);
this.parent.add(paragraph);
if (literalNext) {
this.parent.add(this.literal_block());
}
return [[], 'Body', []];
}
eof(context) {
if ((context != null && !isIterable(context)) || context.length > 0) {
this.blank(null, context, null);
}
return [];
}
/** Definition list item. */
indent(match, context, nextState) {
const definitionlist = new nodes.definition_list();
const [definitionlistitem, blankFinish1] = this.definition_list_item(context);
let blankFinish = blankFinish1;
definitionlist.add(definitionlistitem);
this.parent.add(definitionlist);
const offset = this.rstStateMachine.lineOffset + 1;
const [newlineOffset, blankFinish2] = this.nestedListParse(this.rstStateMachine.inputLines.slice(offset), { inputOffset: this.rstStateMachine.absLineOffset() + 1, node: definitionlist, initialState: 'DefinitionList', blankFinish, blankFinishState: 'Definition', });
blankFinish = blankFinish2;
this.gotoLine(newlineOffset);
if (!blankFinish) {
this.parent.add(this.unindentWarning('Definition list'));
}
return [[], 'Body', []];
}
underline(match, context, nextState) {
/* istanbul ignore if */
if (!Array.isArray(context)) {
throw new Error('Context should be array');
}
const lineno = this.rstStateMachine.absLineNumber();
const title = context[0].trimRight();
const underline = match.result.input.trimRight();
const source = `${title}\n${underline}`;
const messages = [];
if (columnWidth(title) > underline.length) {
if (underline.length < 4) {
if (this.rstStateMachine.matchTitles) {
const msg = this.reporter.info('Possible title underline, too short for the title.\n'
+ "Treating it as ordinary text because it's so short.", [], { line: lineno });
this.parent.add(msg);
throw new TransitionCorrection('text');
}
}
else {
const blocktext = `${context[0]}\n${this.rstStateMachine.line}`;
const msg = this.reporter.warning('Title underline too short.', [new nodes.literal_block(blocktext, blocktext)], { line: lineno });
messages.push(msg);
}
}
if (!this.rstStateMachine.matchTitles) {
const blocktext = `${context[0]}\n${this.rstStateMachine.line}`;
// We need get_source_and_line() here to report correctly
const [src, srcline] = this.rstStateMachine.getSourceAndLine();
const msg = this.reporter.severe('Unexpected section title.', [new nodes.literal_block(blocktext, blocktext)], { source: src, line: srcline });
this.parent.add(messages);
this.parent.add(msg);
return [[], nextState, []];
}
const style = underline[0];
context.length = 0;
this.section({
title, source, style, lineno: lineno - 1, messages,
});
return [[], nextState, []];
}
text(match, context, nextState) {
const startline = this.rstStateMachine.absLineNumber() - 1;
let block;
let msg;
try {
block = this.rstStateMachine.getTextBlock(true);
}
catch (error) {
if (error instanceof UnexpectedIndentationError) {
block = error.block;
const src = error.source;
const srcline = error.lineno;
msg = this.reporter.error('Unexpected indentation.', [], { source: src, line: srcline });
}
else {
throw error;
}
}
const lines = [context, ...(block || [])];
const [pelems, literalNext] = this.paragraph(lines, startline);
this.parent.add(pelems);
if (msg) {
this.parent.add(msg);
}
if (literalNext) {
try {
this.rstStateMachine.nextLine();
}
catch (error) {
if (error instanceof EOFError) {
/* do nothing */
}
else {
throw error;
}
}
this.parent.add(this.literal_block());
}
return [[], nextState, []];
}
/** Return a list of nodes. */
literal_block() {
const [indented, indent, offset, blankFinish] = this.rstStateMachine.getIndented({});
while (indented && indented.length && !indented[indented.length - 1].trim()) {
indented.trimEnd();
}
if (!indented || !indented.length) {
return this.quoted_literal_block();
}
const data = indented.join('\n');
const literalBlock = new nodes.literal_block(data, data);
const [source, line] = this.rstStateMachine.getSourceAndLine(offset + 1);
literalBlock.source = source;
literalBlock.line = line;
const nodelist = [literalBlock];
if (!blankFinish) {
nodelist.push(this.unindentWarning('Literal block'));
}
return nodelist;
}
quoted_literal_block() {
const absLineOffset = this.rstStateMachine.absLineOffset();
const offset = this.rstStateMachine.lineOffset;
const parentNode = new nodes.Element();
// @ts-ignore
const newAbsOffset = this.nestedParse(this.rstStateMachine.inputLines.slice(offset), absLineOffset, parentNode, false, () => NestedStateMachine.createStateMachine(this.rstStateMachine, 'QuotedLiteralBlock', this.rstStateMachine.stateFactory.withStateClasses(['QuotedLiteralBlock'])));
if (newAbsOffset !== undefined) {
this.gotoLine(newAbsOffset);
}
return parentNode.getChildren();
}
definition_list_item(termline) {
const [indented, indent, lineOffset, blankFinish] = this.rstStateMachine.getIndented({});
const itemnode = new nodes.definition_list_item([...termline, ...indented].join('\b'));
const lineno = this.rstStateMachine.absLineNumber() - 1;
[itemnode.source, itemnode.line] = this.rstStateMachine.getSourceAndLine(lineno);
const [termlist, messages] = this.term(termline, lineno);
itemnode.add(termlist);
const definition = new nodes.definition('', messages);
itemnode.add(definition);
if (termline[0].endsWith('::')) {
definition.add(this.reporter.info('Blank line missing before literal block (after the "::")? '
+ 'Interpreted as a definition list item.', [], { line: lineno + 1 }));
}
this.nestedParse(indented, lineOffset, definition);
return [itemnode, blankFinish];
}
term(lines, lineno) {
const [textNodes, messages] = this.inline_text(lines[0], lineno);
const termNode = new nodes.term(lines[0]);
// [termNode.source,
// termNode.line] = this.rstStateMachine.getSourceAndLine(lineno)
const nodeList = [termNode];
textNodes.forEach((node) => {
if (node instanceof nodes.Text) {
const parts = node.astext().split(RegExps.classifierDelimiterRegexp);
if (parts.length === 1) {
nodeList[nodeList.length - 1].add(node);
}
else {
const text = parts[0].trimRight();
const textnode = new nodes.Text(unescape(text, true));
nodeList[nodeList.length - 1].add(textnode);
parts.slice(1).forEach((part) => {
nodeList.push(new nodes.classifier(unescape(part, false), part));
});
}
}
else {
nodeList[nodeList.length - 1].add(node);
}
});
return [nodeList, messages];
}
}
Text.stateName = 'Text';
export default Text;