« get me outta code hell

hsmusic-wiki - HSMusic - static wiki software cataloguing collaborative creation
about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--src/util/sugar.js53
1 files changed, 39 insertions, 14 deletions
diff --git a/src/util/sugar.js b/src/util/sugar.js
index 46c4879c..344ddfd4 100644
--- a/src/util/sugar.js
+++ b/src/util/sugar.js
@@ -288,8 +288,8 @@ export function cut(text, length = 40) {
   }
 }
 
-// Iterates over regular expression matches within a single- or multiline
-// string, yielding each match as well as:
+// Annotates {index, length} results from another iterator with contextual
+// details, including:
 //
 // * its line and column numbers;
 // * if `formatWhere` is true (the default), a pretty-formatted,
@@ -297,7 +297,7 @@ export function cut(text, length = 40) {
 // * if `getContainingLine` is true, the entire line (or multiple lines)
 //   of text containing the match.
 //
-export function* matchMultiline(content, matchRegexp, {
+export function* iterateMultiline(content, iterator, {
   formatWhere = true,
   getContainingLine = false,
 } = {}) {
@@ -308,24 +308,25 @@ export function* matchMultiline(content, matchRegexp, {
   let startOfLine = 0;
   let previousIndex = 0;
 
-  const countLineBreaks = (offset, range) => {
+  const countLineBreaks = (index, length) => {
+    const range = content.slice(index, index + length);
     const lineBreaks = Array.from(range.matchAll(lineRegexp));
     if (!empty(lineBreaks)) {
       lineNumber += lineBreaks.length;
-      startOfLine = offset + lineBreaks.at(-1).index + 1;
+      startOfLine = index + lineBreaks.at(-1).index + 1;
     }
   };
 
-  for (const match of content.matchAll(matchRegexp)) {
-    countLineBreaks(
-      previousIndex,
-      content.slice(previousIndex, match.index));
+  for (const result of iterator) {
+    const {index, length} = result;
+
+    countLineBreaks(previousIndex, index - previousIndex);
 
     const matchStartOfLine = startOfLine;
 
-    previousIndex = match.index + match[0].length;
+    previousIndex = index + length;
 
-    const columnNumber = match.index - startOfLine;
+    const columnNumber = index - startOfLine;
 
     let where = null;
     if (formatWhere) {
@@ -333,10 +334,10 @@ export function* matchMultiline(content, matchRegexp, {
         colors.yellow(
           (isMultiline
             ? `line: ${lineNumber + 1}, col: ${columnNumber + 1}`
-            : `pos: ${match.index + 1}`));
+            : `pos: ${index + 1}`));
     }
 
-    countLineBreaks(match.index, match[0]);
+    countLineBreaks(index, length);
 
     let containingLine = null;
     if (getContainingLine) {
@@ -356,7 +357,7 @@ export function* matchMultiline(content, matchRegexp, {
     }
 
     yield {
-      match,
+      ...result,
       lineNumber,
       columnNumber,
       where,
@@ -365,6 +366,30 @@ export function* matchMultiline(content, matchRegexp, {
   }
 }
 
+// Iterates over regular expression matches within a single- or multiline
+// string, yielding each match as well as contextual details; this accepts
+// the same options (and provides the same context) as iterateMultiline.
+export function* matchMultiline(content, matchRegexp, options) {
+  const matchAllIterator =
+    content.matchAll(matchRegexp);
+
+  const cleanMatchAllIterator =
+    (function*() {
+      for (const match of matchAllIterator) {
+        yield {
+          index: match.index,
+          length: match[0].length,
+          match,
+        };
+      }
+    })();
+
+  const multilineIterator =
+    iterateMultiline(content, cleanMatchAllIterator, options);
+
+  yield* multilineIterator;
+}
+
 // Binds default values for arguments in a {key: value} type function argument
 // (typically the second argument, but may be overridden by providing a
 // [bindOpts.bindIndex] argument). Typically useful for preparing a function for