Skip to content

Commit

Permalink
feat(string): add escape helpers
Browse files Browse the repository at this point in the history
- `escapeFull()` replaces special characters like newline with two-character literals of their escape sequences like `"\n"`

- `escapeTerse()` is converts special characters into single-character unicode symbols, to preserve string length. Importantly, newline becomes `"¶"` and tab becomes `"⇥"`
  • Loading branch information
pskfyi committed May 28, 2023
1 parent b53a333 commit 3b5e820
Show file tree
Hide file tree
Showing 4 changed files with 68 additions and 0 deletions.
2 changes: 2 additions & 0 deletions readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -386,6 +386,7 @@ String-related utilities.
import {
dedent,
elideStart, // and others
escapeTerse,
indent,
mostConsecutive,
sequences,
Expand All @@ -397,6 +398,7 @@ import {
dedent(" a\n b\n c"); // "a\n b\n c"
indent("a\nb\nc", 2); // " a\n b\n c"
elideStart("1234567890", { maxLength: 8 }); // "…4567890"
escapeTerse("\t\t\n"); // "⇥⇥¶"
splitOnFirst("/", "a/b/c"); // ["a", "b/c"]
splitOn(3, "\n", "a\nb\nc\nd\ne"); // ["a", "b", "c", "d\ne"]
sequences("A", "ABAACA"); // ["A", "AA", "A"]
Expand Down
34 changes: 34 additions & 0 deletions string/escape.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import { assertEquals, describe, it } from "../_deps/testing.ts";
import { escapeFull, escapeTerse } from "./escape.ts";

describe("escapeFull", () => {
it("escapes newlines", () => {
assertEquals(escapeFull("\n\n"), "\\n\\n");
assertEquals(escapeFull("\r\r"), "\\r\\r");
assertEquals(escapeFull("\r\n\r\n"), "\\r\\n\\r\\n");
});

it("escapes tabs", () => assertEquals(escapeFull("\t\t"), "\\t\\t"));

it("escapes vertical tabs", () => assertEquals(escapeFull("\v\v"), "\\v\\v"));

it("escapes form feeds", () => assertEquals(escapeFull("\f\f"), "\\f\\f"));

it("escapes backspaces", () => assertEquals(escapeFull("\b\b"), "\\b\\b"));
});

describe("escapeTerse", () => {
it("escapes newlines", () => {
assertEquals(escapeTerse("\n\n"), "¶¶");
assertEquals(escapeTerse("\r\r"), "␍␍");
assertEquals(escapeTerse("\r\n\r\n"), "␍¶␍¶");
});

it("escapes tabs", () => assertEquals(escapeTerse("\t\t"), "⇥⇥"));

it("escapes vertical tabs", () => assertEquals(escapeTerse("\v\v"), "␋␋"));

it("escapes form feeds", () => assertEquals(escapeTerse("\f\f"), "␌␌"));

it("escapes backspaces", () => assertEquals(escapeTerse("\b\b"), "␈␈"));
});
31 changes: 31 additions & 0 deletions string/escape.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
/** Replaces special characters with their escape sequences.
*
* Note: `escape` is a global function in JavaScript, so we use `escapeFull`. */
export function escapeFull(str: string): string {
return str
.replaceAll("\n", "\\n")
.replaceAll("\r", "\\r")
.replaceAll("\t", "\\t")
.replaceAll("\f", "\\f")
.replaceAll("\v", "\\v")
.replaceAll("\b", "\\b");
}

/** Replaces special characters with single Unicode symbols that represent
* them, ensuring that the output length is the same as the input length.
*
* - `\n` becomes `¶`
* - `\r` becomes `␍`
* - `\t` becomes `⇥`
* - `\f` becomes `␌`
* - `\v` becomes `␋`
* - `\b` becomes `␈` */
export function escapeTerse(str: string): string {
return str
.replaceAll("\n", "¶")
.replaceAll("\r", "␍")
.replaceAll("\t", "⇥")
.replaceAll("\f", "␌")
.replaceAll("\v", "␋")
.replaceAll("\b", "␈");
}
1 change: 1 addition & 0 deletions string/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,5 @@ export * from "./sequence.ts";
export * from "./dedent.ts";
export * from "./indent.ts";
export * from "./elide.ts";
export * from "./escape.ts";
export * from "./Text.ts";

0 comments on commit 3b5e820

Please sign in to comment.