UNPKG

@woosh/meep-engine

Version:

Pure JavaScript game engine. Fully featured and production ready.

259 lines (201 loc) • 5.58 kB
import { assert } from "../assert.js"; import { string_repeat } from "../primitives/strings/string_repeat.js"; class Line { /** * * @param {string} text * @param {number} indent */ constructor(text, indent) { assert.isString(text, 'text'); assert.isNonNegativeInteger(indent, 'index'); /** * * @type {string} */ this.text = text; /** * * @type {number} */ this.indentation = indent; } toString() { return `Line{ indentation=${this.indentation}, text="${this.text}" }` } } const DEFAULT_INDENT_SPACES = 4; /** * Useful for generating formatted snippets of code. * @example * const b = new LineBuilder(); * b.add("function hello(){"); * b.indent(); * b.add("return \"hello world\";"); * b.dedent(); * b.add("}"); * b.build(); * // function hello(){ * // return "hello world"; * // } */ class LineBuilder { /** * * @type {Line[]} */ #lines = []; /** * Current indent level * @type {number} */ #indentation = 0; /** * Current indentation level * Mainly intended for testing * @return {number} */ get indentation() { return this.#indentation; } /** * TODO replace with indent string, that is tab, space or any combination or something else entirely * @private * @type {number} */ indentSpaces = DEFAULT_INDENT_SPACES; /** * Number of lines * @return {number} */ get count() { return this.#lines.length; } /** * Substring test of per-line basis. A match can only span a single line, so multi-line matches will not be found. * @return {boolean} * @param {string} term */ containsSubstring(term) { assert.isString(term, 'term'); const lines = this.#lines; const n = lines.length; for (let i = 0; i < n; i++) { const line = lines[i]; const termIndex = line.text.indexOf(term); if (termIndex !== -1) { return true; } } return false; } /** * * @returns {LineBuilder} */ indent() { this.#indentation++; return this; } /** * NOTE: clamps indentation to 0 (can't go negative) * @returns {LineBuilder} */ dedent() { // clamp to 0 this.#indentation = Math.max(0, this.#indentation - 1); return this; } /** * Adds an entire new line with a given text. * Inherits the current indentation level. * @param {string} line_text * @returns {LineBuilder} */ add(line_text) { const line = new Line(line_text, this.#indentation); this.#lines.push(line); return this; } /** * Appends text to the last line. * * @param {string} text * @returns {void} * @example * const b = new LineBuilder(); * b.add("hello"); * b.extend(" world"); * b.build(); // "hello world" * */ extend(text) { assert.isString(text, 'text'); const lines = this.#lines; const last_index = lines.length - 1; if (last_index < 0) { throw new Error("No lines to append to"); } lines[last_index].text += text; } /** * * @param {LineBuilder} lines */ addLines(lines) { assert.defined(lines, 'lines'); const other_lines = lines.#lines; const other_line_count = other_lines.length; for (let i = 0; i < other_line_count; i++) { const otherLine = other_lines[i]; const line = new Line(otherLine.text, otherLine.indentation + this.#indentation); this.#lines.push(line); } } clear() { this.#lines = []; this.#indentation = 0; } /** * Renders out an indented string of lines * @returns {string} */ build() { const result = []; const lines = this.#lines; const line_count = lines.length; const indent_string = string_repeat(' ', this.indentSpaces); for (let i = 0; i < line_count; i++) { const line = lines[i]; let indentString = ''; const indent_count = line.indentation; for (let j = 0; j < indent_count; j++) { indentString += indent_string; } result.push(indentString + line.text); } return result.join('\n'); } /** * @param {string} text * @param {string} [line_separator] defaults to new-line character * @returns {LineBuilder} */ static fromText(text, line_separator = '\n') { assert.isString(text, 'text'); assert.isString(line_separator, 'line_separator'); const r = new LineBuilder(); const lines = text.split(line_separator); const n = lines.length; // TODO detect indent for (let i = 0; i < n; i++) { const line_text = lines[i]; r.add(line_text); } return r; } toString() { return this.build(); } } export default LineBuilder;