Skip to content

Commit

Permalink
Change quote utils to class
Browse files Browse the repository at this point in the history
  • Loading branch information
scripthunter7 committed Aug 10, 2023
1 parent 02e795e commit ec0f8b9
Show file tree
Hide file tree
Showing 4 changed files with 116 additions and 107 deletions.
7 changes: 5 additions & 2 deletions packages/agtree/src/ast-utils/scriptlets.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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;
Expand Down
4 changes: 2 additions & 2 deletions packages/agtree/src/converter/cosmetic/scriptlets/adg.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand All @@ -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,
);
Expand Down
196 changes: 104 additions & 92 deletions packages/agtree/src/utils/quotes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
}
}
16 changes: 5 additions & 11 deletions packages/agtree/test/utils/quotes.test.ts
Original file line number Diff line number Diff line change
@@ -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', () => {
Expand Down Expand Up @@ -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);
});
});

Expand Down Expand Up @@ -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);
});
});

Expand Down Expand Up @@ -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);
});
});

Expand Down Expand Up @@ -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);
});
});
});

0 comments on commit ec0f8b9

Please sign in to comment.