« get me outta code hell

tui-lib - Pure Node.js library for making visual command-line programs (ala vim, ncdu)
about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--util/ansi.js113
1 files changed, 49 insertions, 64 deletions
diff --git a/util/ansi.js b/util/ansi.js
index 5902b70..94de1a3 100644
--- a/util/ansi.js
+++ b/util/ansi.js
@@ -119,7 +119,7 @@ const ansi = {
   },
 
   interpret(text, scrRows, scrCols, {
-    oldChars = null, oldLastCharAttributes = null,
+    oldChars = null, oldLastChar = null,
     oldCursorRow = 0, oldCursorCol = 0, oldShowCursor = true
   } = {}) {
     // Interprets the given ansi code, more or less.
@@ -129,17 +129,7 @@ const ansi = {
       char: ' '
     }
 
-    const numChars = scrRows * scrCols
-    const chars = new Array(numChars * 2)
-
-    const fillBlank = () => {
-      for (let i = 0; i < numChars; i++) {
-        chars[i * 2] = ' '
-        chars[i * 2 + 1] = []
-      }
-    }
-
-    fillBlank()
+    const chars = new Array(scrRows * scrCols).fill(blank)
 
     let showCursor = true
     let cursorRow = 1
@@ -195,7 +185,7 @@ const ansi = {
         if (text[charI] === 'J') {
           // ESC[2J - erase whole display
           if (args[0] === '2') {
-            fillBlank()
+            chars.fill(blank)
             charI += 3
             cursorCol = 1
             cursorRow = 1
@@ -211,7 +201,7 @@ const ansi = {
 
           // ESC[0J - erase to end
           else if (args.length === 0 || args[0] === '0') {
-            for (let i = cursorIndex; i < numChars; i++) {
+            for (let i = cursorIndex; i < chars.length; i++) {
               chars[i * 2] = ' '
               chars[i * 2 + 1] = []
             }
@@ -269,8 +259,10 @@ const ansi = {
         continue
       }
 
-      chars[cursorIndex * 2] = text[charI]
-      chars[cursorIndex * 2 + 1] = attributes.slice()
+      chars[cursorIndex] = {
+        char: text[charI],
+        attributes: attributes.slice()
+      }
 
       cursorCol++
 
@@ -296,52 +288,52 @@ const ansi = {
       differences.push(0)
       differences.push(newChars.slice())
     } else {
-      let curChars = null
+      const charsEqual = (oldChar, newChar) => {
+        // TODO: Check attributes.
 
-      for (let i = 0; i < numChars; i++) {
-        const oldChar = oldChars[i * 2]
-        const newChar = newChars[i * 2]
-
-        let charsEqual = true
+        if (oldChar.char !== newChar.char) {
+          return false
+        }
 
-        if (oldChars[i * 2] !== newChars[i * 2]) {
-          charsEqual = false
-        } else {
-          let oldAttrs = oldChars[i * 2 + 1].slice()
-          let newAttrs = newChars[i * 2 + 1].slice()
+        let oldAttrs = oldChar.attributes.slice()
+        let newAttrs = newChar.attributes.slice()
 
-          while (newAttrs.length) {
-            const attr = newAttrs.shift()
-            if (oldAttrs.includes(attr)) {
-              oldAttrs.splice(oldAttrs.indexOf(attr), 1)
-            } else {
-              charsEqual = false
-              break
-            }
+        while (newAttrs.length) {
+          const attr = newAttrs.shift()
+          if (oldAttrs.includes(attr)) {
+            oldAttrs.splice(oldAttrs.indexOf(attr), 1)
+          } else {
+            return false
           }
+        }
 
-          if (charsEqual) {
-            oldAttrs = oldChars[i * 2 + 1].slice()
-            newAttrs = newChars[i * 2 + 1].slice()
-
-            while (oldAttrs.length) {
-              const attr = oldAttrs.shift()
-              if (newAttrs.includes(attr)) {
-                newAttrs.splice(newAttrs.indexOf(attr), 1)
-              } else {
-                charsEqual = false
-                break
-              }
-            }
+        oldAttrs = oldChar.attributes.slice()
+        newAttrs = newChar.attributes.slice()
+
+        while (oldAttrs.length) {
+          const attr = oldAttrs.shift()
+          if (newAttrs.includes(attr)) {
+            newAttrs.splice(newAttrs.indexOf(attr), 1)
+          } else {
+            return false
           }
         }
 
+        return true
+      }
+
+      let curChars = null
+
+      for (let i = 0; i < chars.length; i++) {
+        const oldChar = oldChars[i]
+        const newChar = newChars[i]
+
         // TODO: Some sort of "distance" before we should clear curDiff?
         // It may take *less* characters if this diff and the next are merged
         // (entering a single character is smaller than the length of the code
         // used to move past that character). Probably not very significant of
         // an impact, though.
-        if (charsEqual) {
+        if (charsEqual(oldChar, newChar)) {
           curChars = null
         } else {
           if (curChars === null) {
@@ -349,22 +341,17 @@ const ansi = {
             differences.push(i, curChars)
           }
 
-          const curAttrs = newChars[i * 2 + 1]
-          curChars.push(newChar, curAttrs)
+          curChars.push(newChar)
         }
       }
     }
 
     // Character concatenation -----------
 
-    /*
     let lastChar = oldLastChar || {
       char: '',
       attributes: []
     }
-    */
-
-    let lastCharAttributes = []
 
     const result = []
 
@@ -376,15 +363,13 @@ const ansi = {
       const row = (i - col) / scrCols
       result.push(ansi.moveCursor(row, col))
 
-      for (let charI = 0; charI < chars.length / 2; charI++) {
-        const charAttributes = chars[charI * 2 + 1]
-
+      for (const char of chars) {
         const newAttributes = (
-          charAttributes.filter(attr => !(lastCharAttributes.includes(attr)))
+          char.attributes.filter(attr => !(lastChar.attributes.includes(attr)))
         )
 
         const removedAttributes = (
-          lastCharAttributes.filter(attr => !(charAttributes.includes(attr)))
+          lastChar.attributes.filter(attr => !(char.attributes.includes(attr)))
         )
 
         // The only way to practically remove any character attribute is to
@@ -392,14 +377,14 @@ const ansi = {
         // If we do that, there's no need to add new attributes.
         if (removedAttributes.length) {
           result.push(ansi.resetAttributes())
-          result.push(`${ESC}[${charAttributes.join(';')}m`)
+          result.push(`${ESC}[${char.attributes.join(';')}m`)
         } else if (newAttributes.length) {
           result.push(`${ESC}[${newAttributes.join(';')}m`)
         }
 
-        result.push(chars[charI * 2])
+        result.push(char.char)
 
-        lastCharAttributes = charAttributes
+        lastChar = char
       }
     }
 
@@ -422,7 +407,7 @@ const ansi = {
       oldCursorRow: cursorRow,
       oldCursorCol: cursorCol,
       oldShowCursor: showCursor,
-      oldLastCharAttributes: lastCharAttributes.slice(),
+      oldLastChar: Object.assign({}, lastChar),
       screen: result.join('')
     }
   }