| import { isWhiteSpace } from './characterClasses.mjs'; |
| /** |
| * Produces the value of a block string from its parsed raw value, similar to |
| * CoffeeScript's block string, Python's docstring trim or Ruby's strip_heredoc. |
| * |
| * This implements the GraphQL spec's BlockStringValue() static algorithm. |
| * |
| * @internal |
| */ |
|
|
| export function dedentBlockStringLines(lines) { |
| var _firstNonEmptyLine2; |
|
|
| let commonIndent = Number.MAX_SAFE_INTEGER; |
| let firstNonEmptyLine = null; |
| let lastNonEmptyLine = -1; |
|
|
| for (let i = 0; i < lines.length; ++i) { |
| var _firstNonEmptyLine; |
|
|
| const line = lines[i]; |
| const indent = leadingWhitespace(line); |
|
|
| if (indent === line.length) { |
| continue; // skip empty lines |
| } |
|
|
| firstNonEmptyLine = |
| (_firstNonEmptyLine = firstNonEmptyLine) !== null && |
| _firstNonEmptyLine !== void 0 |
| ? _firstNonEmptyLine |
| : i; |
| lastNonEmptyLine = i; |
|
|
| if (i !== 0 && indent < commonIndent) { |
| commonIndent = indent; |
| } |
| } |
|
|
| return lines // Remove common indentation from all lines but first. |
| .map((line, i) => (i === 0 ? line : line.slice(commonIndent))) // Remove leading and trailing blank lines. |
| .slice( |
| (_firstNonEmptyLine2 = firstNonEmptyLine) !== null && |
| _firstNonEmptyLine2 !== void 0 |
| ? _firstNonEmptyLine2 |
| : 0, |
| lastNonEmptyLine + 1, |
| ); |
| } |
|
|
| function leadingWhitespace(str) { |
| let i = 0; |
|
|
| while (i < str.length && isWhiteSpace(str.charCodeAt(i))) { |
| ++i; |
| } |
|
|
| return i; |
| } |
| /** |
| * @internal |
| */ |
|
|
| export function isPrintableAsBlockString(value) { |
| if (value === '') { |
| return true; // empty string is printable |
| } |
|
|
| let isEmptyLine = true; |
| let hasIndent = false; |
| let hasCommonIndent = true; |
| let seenNonEmptyLine = false; |
|
|
| for (let i = 0; i < value.length; ++i) { |
| switch (value.codePointAt(i)) { |
| case 0x0000: |
| case 0x0001: |
| case 0x0002: |
| case 0x0003: |
| case 0x0004: |
| case 0x0005: |
| case 0x0006: |
| case 0x0007: |
| case 0x0008: |
| case 0x000b: |
| case 0x000c: |
| case 0x000e: |
| case 0x000f: |
| return false; |
| // Has non-printable characters |
|
|
| case 0x000d: |
| // \r |
| return false; |
| // Has \r or \r\n which will be replaced as \n |
|
|
| case 10: |
| // \n |
| if (isEmptyLine && !seenNonEmptyLine) { |
| return false; // Has leading new line |
| } |
|
|
| seenNonEmptyLine = true; |
| isEmptyLine = true; |
| hasIndent = false; |
| break; |
|
|
| case 9: // \t |
|
|
| case 32: |
| // <space> |
| hasIndent || (hasIndent = isEmptyLine); |
| break; |
|
|
| default: |
| hasCommonIndent && (hasCommonIndent = hasIndent); |
| isEmptyLine = false; |
| } |
| } |
|
|
| if (isEmptyLine) { |
| return false; // Has trailing empty lines |
| } |
|
|
| if (hasCommonIndent && seenNonEmptyLine) { |
| return false; // Has internal indent |
| } |
|
|
| return true; |
| } |
| /** |
| * Print a block string in the indented block form by adding a leading and |
| * trailing blank line. However, if a block string starts with whitespace and is |
| * a single-line, adding a leading blank line would strip that whitespace. |
| * |
| * @internal |
| */ |
|
|
| export function printBlockString(value, options) { |
| const escapedValue = value.replace(/"""/g, '\\"""'); // Expand a block string's raw value into independent lines. |
|
|
| const lines = escapedValue.split(/\r\n|[\n\r]/g); |
| const isSingleLine = lines.length === 1; // If common indentation is found we can fix some of those cases by adding leading new line |
|
|
| const forceLeadingNewLine = |
| lines.length > 1 && |
| lines |
| .slice(1) |
| .every((line) => line.length === 0 || isWhiteSpace(line.charCodeAt(0))); // Trailing triple quotes just looks confusing but doesn't force trailing new line |
|
|
| const hasTrailingTripleQuotes = escapedValue.endsWith('\\"""'); // Trailing quote (single or double) or slash forces trailing new line |
|
|
| const hasTrailingQuote = value.endsWith('"') && !hasTrailingTripleQuotes; |
| const hasTrailingSlash = value.endsWith('\\'); |
| const forceTrailingNewline = hasTrailingQuote || hasTrailingSlash; |
| const printAsMultipleLines = |
| !(options !== null && options !== void 0 && options.minimize) && // add leading and trailing new lines only if it improves readability |
| (!isSingleLine || |
| value.length > 70 || |
| forceTrailingNewline || |
| forceLeadingNewLine || |
| hasTrailingTripleQuotes); |
| let result = ''; // Format a multi-line block quote to account for leading space. |
|
|
| const skipLeadingNewLine = isSingleLine && isWhiteSpace(value.charCodeAt(0)); |
|
|
| if ((printAsMultipleLines && !skipLeadingNewLine) || forceLeadingNewLine) { |
| result += '\n'; |
| } |
|
|
| result += escapedValue; |
|
|
| if (printAsMultipleLines || forceTrailingNewline) { |
| result += '\n'; |
| } |
|
|
| return '"""' + result + '"""'; |
| } |