cmark

My personal build of CMark ✏️

Commit
d6a73928f95f89f78fdc005bd9548ed198f3794d
Parent
4a4b050a54382f15b9e62c2deb1111bd9d20b663
Author
John MacFarlane <jgm@berkeley.edu>
Date

Added processEmphasis, run at end of parseInlines.

Diffstat

1 file changed, 140 insertions, 13 deletions

Status File Name N° Changes Insertions Deletions
Modified js/lib/inlines.js 153 140 13
diff --git a/js/lib/inlines.js b/js/lib/inlines.js
@@ -267,7 +267,6 @@ var parseEmphasis = function(cc,inlines) {
     var startpos = this.pos;
 
     if (numdelims === 0) {
-        this.pos = startpos;
         return false;
     }
 
@@ -275,25 +274,150 @@ var parseEmphasis = function(cc,inlines) {
     inlines.push(Str(this.subject.slice(startpos, this.pos)));
 
     // Add entry to stack for this opener
-    this.emphasis_openers = { cc: cc,
-                              numdelims: numdelims,
-                              pos: inlines.length - 1,
-                              previous: this.emphasis_openers,
-                              next: null,
-                              can_open: res.can_open,
-                              can_close: res.can_close};
+    this.delimiters = { cc: cc,
+                        numdelims: numdelims,
+                        pos: inlines.length - 1,
+                        previous: this.delimiters,
+                        next: null,
+                        can_open: res.can_open,
+                        can_close: res.can_close};
+    if (this.delimiters.previous != null) {
+        this.delimiters.previous.next = this.delimiters;
+    }
 
     return true;
 
 };
 
+var removeDelimiter = function(delim) {
+    if (delim.previous !== null) {
+        delim.previous.next = delim.next;
+    }
+    if (delim.next === null) {
+        // top of stack
+        this.delimiters = delim.previous;
+    } else {
+        delim.next.previous = delim.previous;
+    }
+}
+
+var removeGaps = function(inlines) {
+    // remove gaps from inlines
+    var i, j;
+    j = 0;
+    for (i = 0 ; i < inlines.length; i++) {
+        if (inlines[i] !== null) {
+            inlines[j] = inlines[i];
+            j++;
+        }
+    }
+    inlines.splice(j);
+}
+
+var processEmphasis = function(inlines, stack_bottom) {
+    var opener, closer;
+    var opener_inl, closer_inl;
+    var nextstack, tempstack;
+    var use_delims;
+    var contents;
+    var tmp;
+    var emph;
+    var i,j;
+
+    // find first closer above stack_bottom:
+    closer = this.delimiters;
+    while (closer !== null && closer.previous !== stack_bottom) {
+        closer = closer.previous;
+    }
+    // move forward, looking for closers, and handling each
+    while (closer !== null) {
+        if (closer.can_close && (closer.cc === C_UNDERSCORE || closer.cc === C_ASTERISK)) {
+            // found emphasis closer. now look back for first matching opener:
+            opener = closer.previous;
+            while (opener !== null && opener !== stack_bottom) {
+                if (opener.cc === closer.cc && opener.can_open) {
+                    break;
+                }
+                opener = opener.previous;
+            }
+            if (opener != null && opener != stack_bottom) {
+                // calculate actual number of delimiters used from this closer
+                if (closer.numdelims < 3 || opener.numdelims < 3) {
+                    use_delims = closer.numdelims <= opener.numdelims ?
+                        closer.numdelims : opener.numdelims;
+                } else {
+                    use_delims = closer.numdelims % 2 == 0 ? 2 : 1;
+                }
+
+                opener_inl = inlines[opener.pos];
+                closer_inl = inlines[closer.pos];
+
+                // remove used delimiters from stack elts and inlines
+                opener.numdelims -= use_delims;
+                closer.numdelims -= use_delims;
+                opener_inl.c = opener_inl.c.slice(0, opener_inl.c.length - use_delims);
+                closer_inl.c = closer_inl.c.slice(0, closer_inl.c.length - use_delims);
+
+                // build contents for new emph element
+                contents = inlines.slice(opener.pos + 1, closer.pos);
+                removeGaps(contents);
+
+                emph = use_delims === 1 ? Emph(contents) : Strong(contents);
+
+                // insert into list of inlines
+                inlines[opener.pos + 1] = emph;
+                for (i = opener.pos + 2; i < closer.pos; i++) {
+                    inlines[i] = null;
+                }
+
+                // remove elts btw opener and closer in delimiters stack
+                tempstack = closer.previous;
+                while (tempstack !== null && tempstack !== opener) {
+                    nextstack = tempstack.previous;
+                    // TODO add remove_delimiter!
+                    this.removeDelimiter(tempstack);
+                    tempstack = nextstack;
+                }
+
+                // if opener has 0 delims, remove it and the inline
+                if (opener.numdelims === 0) {
+                    inlines[opener.pos] = null;
+                    this.removeDelimiter(opener);
+                }
+
+                if (closer.numdelims === 0) {
+                    inlines[closer.pos] = null;
+                    tempstack = closer.next;
+                    this.removeDelimiter(closer);
+                    closer = tempstack;
+                }
+
+
+            } else {
+                closer = closer.next;
+            }
+
+        } else {
+            closer = closer.next;
+        }
+
+    }
+
+    removeGaps(inlines);
+
+    // remove all delimiters
+    while (this.delimiters != stack_bottom) {
+        this.removeDelimiter(this.delimiters);
+    }
+};
+
 /* TODO
     var numdelims = res.numdelims;
     var usedelims;
     if (res.can_close) {
 
       // Walk the stack and find a matching opener, if possible
-      var opener = this.emphasis_openers;
+      var opener = this.delimiters;
       while (opener) {
 
         if (opener.cc === cc) { // we have a match!
@@ -311,7 +435,7 @@ var parseEmphasis = function(cc,inlines) {
             inlines[opener.pos] = X(inlines.slice(opener.pos + 1));
             inlines.splice(opener.pos + 1, inlines.length - (opener.pos + 1));
             // Remove entries after this, to prevent overlapping nesting:
-            this.emphasis_openers = opener.previous;
+            this.delimiters = opener.previous;
             return true;
 
           } else if (opener.numdelims > usedelims) { // only some openers used
@@ -323,7 +447,7 @@ var parseEmphasis = function(cc,inlines) {
             inlines[opener.pos + 1] = X(inlines.slice(opener.pos + 1));
             inlines.splice(opener.pos + 2, inlines.length - (opener.pos + 2));
             // Remove entries after this, to prevent overlapping nesting:
-            this.emphasis_openers = opener;
+            this.delimiters = opener;
             return true;
 
           } else { // usedelims > opener.numdelims, should never happen
@@ -671,10 +795,11 @@ var parseInlines = function(s, refmap) {
     this.subject = s;
     this.pos = 0;
     this.refmap = refmap || {};
-    this.emphasis_openers = null;
+    this.delimiters = null;
     var inlines = [];
     while (this.parseInline(inlines)) {
     }
+    this.processEmphasis(inlines, null);
     return inlines;
 };
 
@@ -683,7 +808,7 @@ function InlineParser(){
     return {
         subject: '',
         label_nest_level: 0, // used by parseLinkLabel method
-        emphasis_openers: null,  // used by parseEmphasis method
+        delimiters: null,  // used by parseEmphasis method
         pos: 0,
         refmap: {},
         match: match,
@@ -706,6 +831,8 @@ function InlineParser(){
         parseImage: parseImage,
         parseReference: parseReference,
         parseInline: parseInline,
+        processEmphasis: processEmphasis,
+        removeDelimiter: removeDelimiter,
         parse: parseInlines
     };
 }