diff --git a/packages/agtree/src/ast-utils/scriptlets.ts b/packages/agtree/src/ast-utils/scriptlets.ts index f4e45feacf..c04bd57404 100644 --- a/packages/agtree/src/ast-utils/scriptlets.ts +++ b/packages/agtree/src/ast-utils/scriptlets.ts @@ -5,7 +5,7 @@ import cloneDeep from 'clone-deep'; import { type ParameterList } from '../parser/common'; -import { type QuoteType, setStringQuoteType } from '../utils/quotes'; +import { type QuoteType, QuoteUtils } from '../utils/quotes'; /** * Get name of the scriptlet from the scriptlet node @@ -56,7 +56,10 @@ export function setScriptletQuoteType(scriptletNode: ParameterList, quoteType: Q const scriptletNodeClone = cloneDeep(scriptletNode); for (let i = 0; i < scriptletNodeClone.children.length; i += 1) { - scriptletNodeClone.children[i].value = setStringQuoteType(scriptletNodeClone.children[i].value, quoteType); + scriptletNodeClone.children[i].value = QuoteUtils.setStringQuoteType( + scriptletNodeClone.children[i].value, + quoteType, + ); } return scriptletNodeClone; diff --git a/packages/agtree/src/converter/cosmetic/scriptlets/adg.ts b/packages/agtree/src/converter/cosmetic/scriptlets/adg.ts index 906eb23877..c7de5f2fdb 100644 --- a/packages/agtree/src/converter/cosmetic/scriptlets/adg.ts +++ b/packages/agtree/src/converter/cosmetic/scriptlets/adg.ts @@ -4,7 +4,7 @@ import cloneDeep from 'clone-deep'; -import { QuoteType, setStringQuoteType } from '../../../utils/quotes'; +import { QuoteType, QuoteUtils } from '../../../utils/quotes'; import { getScriptletName, setScriptletName, setScriptletQuoteType } from '../../../ast-utils/scriptlets'; import { type ParameterList } from '../../../parser/common'; import { type ConverterFunction } from '../../base-interfaces/converter-function'; @@ -29,7 +29,7 @@ export class AdgScriptletConverter { */ private static convertToAdg(scriptletNode: ParameterList, prefix: Prefix): ParameterList { // Remove possible quotes just to make it easier to work with the scriptlet name - const scriptletName = setStringQuoteType( + const scriptletName = QuoteUtils.setStringQuoteType( getScriptletName(scriptletNode), QuoteType.None, ); diff --git a/packages/agtree/src/utils/quotes.ts b/packages/agtree/src/utils/quotes.ts index d7bcc2ac16..9c541447c8 100644 --- a/packages/agtree/src/utils/quotes.ts +++ b/packages/agtree/src/utils/quotes.ts @@ -30,119 +30,131 @@ export enum QuoteType { } /** - * Escape all unescaped occurrences of the character - * - * @param string String to escape - * @param char Character to escape - * @returns Escaped string + * Utility functions for working with quotes */ -export function escapeUnescapedOccurrences(string: string, char: string): string { - let result = EMPTY; +export class QuoteUtils { + /** + * Escape all unescaped occurrences of the character + * + * @param string String to escape + * @param char Character to escape + * @returns Escaped string + */ + public static escapeUnescapedOccurrences(string: string, char: string): string { + let result = EMPTY; + + for (let i = 0; i < string.length; i += 1) { + if (string[i] === char && (i === 0 || string[i - 1] !== ESCAPE_CHARACTER)) { + result += ESCAPE_CHARACTER; + } - for (let i = 0; i < string.length; i += 1) { - if (string[i] === char && (i === 0 || string[i - 1] !== ESCAPE_CHARACTER)) { - result += ESCAPE_CHARACTER; + result += string[i]; } - result += string[i]; + return result; } - return result; -} - -/** - * Unescape all single escaped occurrences of the character - * - * @param string String to unescape - * @param char Character to unescape - * @returns Unescaped string - */ -export function unescapeSingleEscapedOccurrences(string: string, char: string): string { - let result = EMPTY; + /** + * Unescape all single escaped occurrences of the character + * + * @param string String to unescape + * @param char Character to unescape + * @returns Unescaped string + */ + public static unescapeSingleEscapedOccurrences(string: string, char: string): string { + let result = EMPTY; - for (let i = 0; i < string.length; i += 1) { - if ( - string[i] === char + for (let i = 0; i < string.length; i += 1) { + if ( + string[i] === char && string[i - 1] === ESCAPE_CHARACTER && (i === 1 || string[i - 2] !== ESCAPE_CHARACTER) - ) { - result = result.slice(0, -1); - } - - result += string[i]; - } - - return result; -} + ) { + result = result.slice(0, -1); + } -/** - * Get quote type of the string - * - * @param string String to check - * @returns Quote type of the string - */ -export function getStringQuoteType(string: string): QuoteType { - // Don't check 1-character strings to avoid false positives - if (string.length > 1) { - if (string.startsWith(SINGLE_QUOTE) && string.endsWith(SINGLE_QUOTE)) { - return QuoteType.Single; + result += string[i]; } - if (string.startsWith(DOUBLE_QUOTE) && string.endsWith(DOUBLE_QUOTE)) { - return QuoteType.Double; - } + return result; } - return QuoteType.None; -} - -/** - * Set quote type of the string - * - * @param string String to set quote type of - * @param quoteType Quote type to set - * @returns String with the specified quote type - */ -export function setStringQuoteType(string: string, quoteType: QuoteType): string { - const actualQuoteType = getStringQuoteType(string); - - switch (quoteType) { - case QuoteType.None: - if (actualQuoteType === QuoteType.Single) { - return escapeUnescapedOccurrences(string.slice(1, -1), SINGLE_QUOTE); - } - - if (actualQuoteType === QuoteType.Double) { - return escapeUnescapedOccurrences(string.slice(1, -1), DOUBLE_QUOTE); - } - - return string; - - case QuoteType.Single: - if (actualQuoteType === QuoteType.None) { - return SINGLE_QUOTE + escapeUnescapedOccurrences(string, SINGLE_QUOTE) + SINGLE_QUOTE; + /** + * Get quote type of the string + * + * @param string String to check + * @returns Quote type of the string + */ + public static getStringQuoteType(string: string): QuoteType { + // Don't check 1-character strings to avoid false positives + if (string.length > 1) { + if (string.startsWith(SINGLE_QUOTE) && string.endsWith(SINGLE_QUOTE)) { + return QuoteType.Single; } - if (actualQuoteType === QuoteType.Double) { - // eslint-disable-next-line max-len - return SINGLE_QUOTE + escapeUnescapedOccurrences(unescapeSingleEscapedOccurrences(string.slice(1, -1), DOUBLE_QUOTE), SINGLE_QUOTE) + SINGLE_QUOTE; + if (string.startsWith(DOUBLE_QUOTE) && string.endsWith(DOUBLE_QUOTE)) { + return QuoteType.Double; } + } - return string; - - case QuoteType.Double: - if (actualQuoteType === QuoteType.None) { - return DOUBLE_QUOTE + escapeUnescapedOccurrences(string, DOUBLE_QUOTE) + DOUBLE_QUOTE; - } + return QuoteType.None; + } - if (actualQuoteType !== QuoteType.Double) { + /** + * Set quote type of the string + * + * @param string String to set quote type of + * @param quoteType Quote type to set + * @returns String with the specified quote type + */ + public static setStringQuoteType(string: string, quoteType: QuoteType): string { + const actualQuoteType = QuoteUtils.getStringQuoteType(string); + + switch (quoteType) { + case QuoteType.None: + if (actualQuoteType === QuoteType.Single) { + return QuoteUtils.escapeUnescapedOccurrences(string.slice(1, -1), SINGLE_QUOTE); + } + + if (actualQuoteType === QuoteType.Double) { + return QuoteUtils.escapeUnescapedOccurrences(string.slice(1, -1), DOUBLE_QUOTE); + } + + return string; + + case QuoteType.Single: + if (actualQuoteType === QuoteType.None) { + return SINGLE_QUOTE + QuoteUtils.escapeUnescapedOccurrences(string, SINGLE_QUOTE) + SINGLE_QUOTE; + } + + if (actualQuoteType === QuoteType.Double) { + return SINGLE_QUOTE + + QuoteUtils.escapeUnescapedOccurrences( + QuoteUtils.unescapeSingleEscapedOccurrences(string.slice(1, -1), DOUBLE_QUOTE), + SINGLE_QUOTE, + ) + SINGLE_QUOTE; + } + + return string; + + case QuoteType.Double: + if (actualQuoteType === QuoteType.None) { + return DOUBLE_QUOTE + QuoteUtils.escapeUnescapedOccurrences(string, DOUBLE_QUOTE) + DOUBLE_QUOTE; + } + + if (actualQuoteType !== QuoteType.Double) { // eslint-disable-next-line max-len - return DOUBLE_QUOTE + escapeUnescapedOccurrences(unescapeSingleEscapedOccurrences(string.slice(1, -1), SINGLE_QUOTE), DOUBLE_QUOTE) + DOUBLE_QUOTE; - } + return DOUBLE_QUOTE + + QuoteUtils.escapeUnescapedOccurrences( + QuoteUtils.unescapeSingleEscapedOccurrences(string.slice(1, -1), SINGLE_QUOTE), + DOUBLE_QUOTE, + ) + DOUBLE_QUOTE; + } - return string; + return string; - default: - return string; + default: + return string; + } } } diff --git a/packages/agtree/test/utils/quotes.test.ts b/packages/agtree/test/utils/quotes.test.ts index 2e03c24231..7c0ae8ff8e 100644 --- a/packages/agtree/test/utils/quotes.test.ts +++ b/packages/agtree/test/utils/quotes.test.ts @@ -1,10 +1,4 @@ -import { - escapeUnescapedOccurrences, - unescapeSingleEscapedOccurrences, - getStringQuoteType, - QuoteType, - setStringQuoteType, -} from '../../src/utils/quotes'; +import { QuoteType, QuoteUtils } from '../../src/utils/quotes'; describe('Quote utils', () => { describe('escapeUnescapedOccurrences', () => { @@ -35,7 +29,7 @@ describe('Quote utils', () => { char: 'a', }, ])('should escape \'$char\' in \'$actual\' as \'$expected\'', ({ actual, expected, char }) => { - expect(escapeUnescapedOccurrences(actual, char)).toBe(expected); + expect(QuoteUtils.escapeUnescapedOccurrences(actual, char)).toBe(expected); }); }); @@ -72,7 +66,7 @@ describe('Quote utils', () => { char: 'a', }, ])('should unescape \'$char\' in \'$actual\' as \'$expected\'', ({ actual, expected, char }) => { - expect(unescapeSingleEscapedOccurrences(actual, char)).toBe(expected); + expect(QuoteUtils.unescapeSingleEscapedOccurrences(actual, char)).toBe(expected); }); }); @@ -136,7 +130,7 @@ describe('Quote utils', () => { expected: QuoteType.Double, }, ])('should detect \'$actual\' quotes as \'$expected\'', ({ actual, expected }) => { - expect(getStringQuoteType(actual)).toBe(expected); + expect(QuoteUtils.getStringQuoteType(actual)).toBe(expected); }); }); @@ -269,7 +263,7 @@ describe('Quote utils', () => { quote: QuoteType.Double, }, ])('should apply \'$quote\' quotes to \'$actual\' as \'$expected\'', ({ actual, expected, quote }) => { - expect(setStringQuoteType(actual, quote)).toBe(expected); + expect(QuoteUtils.setStringQuoteType(actual, quote)).toBe(expected); }); }); });