diff --git a/src/pastel/HumanReadableImplementation.re b/src/pastel/HumanReadableImplementation.re
index 0333ceba..9f885594 100644
--- a/src/pastel/HumanReadableImplementation.re
+++ b/src/pastel/HumanReadableImplementation.re
@@ -14,6 +14,7 @@ module HumanReadableLexer =
let length = HumanReadableLexer.length;
let partition = HumanReadableLexer.partition;
+let partition2 = HumanReadableLexer.partition2;
let unformattedText = HumanReadableLexer.unformattedText;
let makeDecorator = name => {
diff --git a/src/pastel/Pastel.rei b/src/pastel/Pastel.rei
index 16739462..51c1a556 100644
--- a/src/pastel/Pastel.rei
+++ b/src/pastel/Pastel.rei
@@ -69,6 +69,7 @@ type modifier = {
let length: string => int;
let partition: (int, string) => (string, string);
+let partition2: (list(int), string) => list(string);
let unformattedText: string => string;
let modifier: modifier;
diff --git a/src/pastel/PastelFactory.re b/src/pastel/PastelFactory.re
index d48f21cf..ee10e3ec 100644
--- a/src/pastel/PastelFactory.re
+++ b/src/pastel/PastelFactory.re
@@ -119,6 +119,14 @@ module Make = (()) => {
| Disabled => DisabledImplementation.partition(s)
};
+ let partition2 = s => {
+ switch (mode^) {
+ | HumanReadable => HumanReadableImplementation.partition2(s)
+ | Terminal => TerminalImplementation.partition2(s)
+ | Disabled => TerminalImplementation.partition2(s)
+ };
+ };
+
let unformattedText = s =>
switch (mode^) {
| HumanReadable => HumanReadableImplementation.unformattedText(s)
diff --git a/src/pastel/PastelLexer.re b/src/pastel/PastelLexer.re
index c1ba9f71..04c2308b 100644
--- a/src/pastel/PastelLexer.re
+++ b/src/pastel/PastelLexer.re
@@ -90,6 +90,112 @@ module Make = (M: {
)
|> String.concat("");
+ type partitionAcc = {
+ activeIndex: int,
+ remainingIndices: list(int),
+ nonTextTokensSeen: list(token),
+ parsedText: list(list(token)),
+ charsSeen: int,
+ activeSection: list(token),
+ queue: list(token),
+ };
+
+ let partition2 = (indices, s) => {
+ let tokens = tokenize(s);
+
+ switch (indices) {
+ | [] => [s]
+ | [h, ...t] =>
+ let initialState = {
+ activeIndex: h,
+ remainingIndices: t,
+ nonTextTokensSeen: [],
+ parsedText: [],
+ charsSeen: 0,
+ activeSection: [],
+ queue: tokens,
+ };
+
+ let step = state =>
+ switch (state.queue) {
+ | [] => state
+ | [token, ...remainingQueue] =>
+ switch (token) {
+ | Starts(_)
+ | Stops(_) => {
+ ...state,
+ nonTextTokensSeen: [token, ...state.nonTextTokensSeen],
+ parsedText:
+ List.map(tokens => [token, ...tokens], state.parsedText),
+ activeSection: [token, ... state.activeSection],
+ queue: remainingQueue,
+ }
+ | Text(text) =>
+ if (state.charsSeen < state.activeIndex) {
+ let textLength = String.length(text);
+ if (state.charsSeen + textLength < state.activeIndex) {
+ {
+ ...state,
+ charsSeen: state.charsSeen + textLength,
+ activeSection: [token, ...state.activeSection],
+ queue: remainingQueue,
+ };
+ } else {
+ let numToTake = state.activeIndex - state.charsSeen;
+ let remaining = String.length(text) - numToTake;
+ let remainingText = String.sub(text, numToTake, remaining);
+ let firstPart = Text(String.sub(text, 0, numToTake));
+
+ switch (state.remainingIndices) {
+ | [] => {
+ ...state,
+ charsSeen: state.charsSeen + textLength,
+ activeSection: [
+ Text(remainingText),
+ ...state.nonTextTokensSeen,
+ ],
+ parsedText: [
+ [firstPart, ...state.activeSection],
+ ...state.parsedText,
+ ],
+ queue: remainingQueue,
+ }
+ | [h, ...t] => {
+ ...state,
+ charsSeen: state.charsSeen + numToTake,
+ activeSection: state.nonTextTokensSeen,
+ parsedText: [
+ [firstPart, ...state.activeSection],
+ ...state.parsedText,
+ ],
+ queue: [Text(remainingText), ...remainingQueue],
+ activeIndex: h,
+ remainingIndices: t,
+ }
+ };
+ };
+ } else {
+ {
+ ...state,
+ activeSection: [token, ...state.activeSection],
+ queue: remainingQueue,
+ };
+ }
+ }
+ };
+
+ let state = ref(initialState);
+ while (state^.queue !== []) {
+ state := step(state^);
+ };
+ let parsedText = [state^.activeSection, ...state^.parsedText]
+ List.map(
+ tokens => tokensToStr(List.rev(tokens)),
+ List.rev(parsedText),
+ );
+ };
+ };
+
/** everything before index is one string, everything else is the other */
let partition = (index, s) => {
let tokens = tokenize(s);
diff --git a/src/pastel/PastelSig.re b/src/pastel/PastelSig.re
index 8c99122a..de175f6d 100644
--- a/src/pastel/PastelSig.re
+++ b/src/pastel/PastelSig.re
@@ -14,6 +14,7 @@ module type PastelSig = {
let useMode: (mode, unit => 'a) => 'a;
let length: string => int;
let partition: (int, string) => (string, string);
+ let partition2: (list(int), string) => list(string);
let unformattedText: string => string;
let modifier: modifier;
diff --git a/src/pastel/TerminalImplementation.re b/src/pastel/TerminalImplementation.re
index 88db7f53..116fde50 100644
--- a/src/pastel/TerminalImplementation.re
+++ b/src/pastel/TerminalImplementation.re
@@ -23,6 +23,7 @@ module TerminalLexer =
let length = TerminalLexer.length;
let partition = TerminalLexer.partition;
+let partition2 = TerminalLexer.partition2;
let unformattedText = TerminalLexer.unformattedText;
let rec reduceTokens = (style: Ansi.style, str, tokens) =>
diff --git a/tests/__snapshots__/Pastel_Pastel_Human_Readable_mode_Pastel_partition.9f56c617.0.snapshot b/tests/__snapshots__/Pastel_Pastel_Human_Readable_mode_Pastel_partition.9f56c617.0.snapshot
new file mode 100644
index 00000000..030326e5
--- /dev/null
+++ b/tests/__snapshots__/Pastel_Pastel_Human_Readable_mode_Pastel_partition.9f56c617.0.snapshot
@@ -0,0 +1,6 @@
+Pastel › Pastel Human Readable mode › Pastel.partition2 › complicated nested formatting
+ooh
+ello
+ wor
+ld!u
+npasteled o_O
diff --git a/tests/__snapshots__/Pastel_Pastel_disabled_mode_Pastel_partition.3369e703.0.snapshot b/tests/__snapshots__/Pastel_Pastel_disabled_mode_Pastel_partition.3369e703.0.snapshot
new file mode 100644
index 00000000..de9e68ee
--- /dev/null
+++ b/tests/__snapshots__/Pastel_Pastel_disabled_mode_Pastel_partition.3369e703.0.snapshot
@@ -0,0 +1,6 @@
+Pastel › Pastel disabled mode › Pastel.partition2 › complicated nested formatting
+ooh
+ello
+ wor
+ld!u
+npasteled o_O
diff --git a/tests/__snapshots__/Pastel_Pastel_terminal_mode_Pastel_partition.f482f7df.0.snapshot b/tests/__snapshots__/Pastel_Pastel_terminal_mode_Pastel_partition.f482f7df.0.snapshot
new file mode 100644
index 00000000..8ed2ae4c
--- /dev/null
+++ b/tests/__snapshots__/Pastel_Pastel_terminal_mode_Pastel_partition.f482f7df.0.snapshot
@@ -0,0 +1,6 @@
+Pastel › Pastel terminal mode › Pastel.partition2 › complicated nested formatting
+\027[2moo\027[22m\027[2m\027[32mh\027[39m\027[22m\027[2m\027[22m
+\027[2m\027[22m\027[2m\027[32mello\027[39m\027[22m\027[2m\027[22m
+\027[2m\027[22m\027[2m\027[32m wor\027[39m\027[22m\027[2m\027[22m
+\027[2m\027[22m\027[2m\027[32mld!\027[39m\027[22m\027[2mu\027[22m
+\027[2m\027[22m\027[2m\027[32m\027[39m\027[22m\027[2mnpasteled o_O\027[22m
diff --git a/tests/__tests__/pastel/Pastel_test.re b/tests/__tests__/pastel/Pastel_test.re
index 13618b37..6b1f3657 100644
--- a/tests/__tests__/pastel/Pastel_test.re
+++ b/tests/__tests__/pastel/Pastel_test.re
@@ -116,20 +116,24 @@ describe("Pastel", ({describe, test}) => {
PastelWithSpecifiedMode.partition(45, input);
expect.string(part1).toEqual(input);
- expect.string(PastelWithSpecifiedMode.unformattedText(part2)).toBeEmpty();
+ expect.string(PastelWithSpecifiedMode.unformattedText(part2)).
+ toBeEmpty();
});
- test(
- "index equal to length should return (s, '')", ({expect}) => {
+ test("index equal to length should return (s, '')", ({expect}) => {
let input =
"Hello world"
;
();
let (part1, part2) =
- PastelWithSpecifiedMode.partition(PastelWithSpecifiedMode.length(input), input);
+ PastelWithSpecifiedMode.partition(
+ PastelWithSpecifiedMode.length(input),
+ input,
+ );
expect.string(part1).toEqual(input);
- expect.string(PastelWithSpecifiedMode.unformattedText(part2)).toBeEmpty();
+ expect.string(PastelWithSpecifiedMode.unformattedText(part2)).
+ toBeEmpty();
});
test("negative index should return ('', s)", ({expect}) => {
let input =
@@ -140,7 +144,8 @@ describe("Pastel", ({describe, test}) => {
let (part1, part2) =
PastelWithSpecifiedMode.partition(-1, input);
- expect.string(PastelWithSpecifiedMode.unformattedText(part1)).toBeEmpty();
+ expect.string(PastelWithSpecifiedMode.unformattedText(part1)).
+ toBeEmpty();
expect.string(part2).toEqual(input);
});
test("index 0 should return ('', s)", ({expect}) => {
@@ -152,10 +157,40 @@ describe("Pastel", ({describe, test}) => {
let (part1, part2) =
PastelWithSpecifiedMode.partition(0, input);
- expect.string(PastelWithSpecifiedMode.unformattedText(part1)).toBeEmpty();
+ expect.string(PastelWithSpecifiedMode.unformattedText(part1)).
+ toBeEmpty();
expect.string(part2).toEqual(input);
});
});
+ describe("Pastel.partition2", ({test}) => {
+ test("unformatted text", ({expect}) => {
+ let result =
+ PastelWithSpecifiedMode.partition2(
+ [4, 6, 9],
+ "hello world!",
+ );
+ expect.lines(result).toEqualLines([
+ "hell",
+ "o ",
+ "wor",
+ "ld!",
+ ]);
+ });
+ test("complicated nested formatting", ({expect}) => {
+ let input =
+
+ "oo"
+
+ "hello world!"
+
+ "unpasteled o_O"
+ ;
+
+ let result =
+ PastelWithSpecifiedMode.partition2([3, 7, 11, 15], input);
+ expect.lines(result).toMatchSnapshot();
+ });
+ });
describe("Pastel.length", ({test}) => {
test("Length with no colors", ({expect}) => {
expect.int(length("")).toBe(0);