javascript – code-prettify does not work with linear code (single line)

Question:

I'm using Google library to format code: Google code-prettify

It works great in almost all cases, but since my text comes in a single string, it doesn't work so well.

It works well for:

<ul>
  <li></li>
</ul>

And it doesn't work for:

<ul><li></li></ul>

My example in JSFiddler: https://jsfiddle.net/dorathoto/3qpwtcoy/8/

I tried to use several JavaScript plugins to format, which in my case is standard XML and none of them were successful.

Answer:

I made an indenter from scratch which worked in your JSFiddle example and in some other tests I did.

It works like this:

Leave the <xmp> tag initially empty and create a hidden div right after it and place the linear code that comes from the database inside this div:

<div id="fonte" style="display: none;">código aqui</div>

The code will iterate through all the nodes inside the div and assemble an array of tags = [] objects, to get the element tag or the text node, which are two different things. It is necessary to distinguish one thing from the other to be able to assemble the indentation correctly.

It then cycles through the object array creating a new HTML in node order with line breaks ( \n ) for each node, and drops that new HTML into the div, replacing what was there. The new HTML tags all go without closing, but the browser closes them all automatically.

Then it takes the new HTML from the div and throws it into the <xmp> , but the last line will be linear with all the automatic closings generated by the browser. Then it is necessary to deal with this. It will take that line and break it into an array ( last_str.match(/<.+?>/g) ).

Then just traverse this array by dynamically adding new lines to the <xmp> with document.createElement() , at the same time assigning the classes in the <li> sequentially (if not the component doesn't recognize it) that goes from L0 to L9 .

But for this to work, you need to use the component callback added to the end of the URL the string &callback=iniciar (where iniciar is the method of the callback function. You can name it whatever you like instead of iniciar ). This callback is called by the syntax:

window.exports = { 
   iniciar: function(){

    // código aqui

   }
}

The function is executed after the component has rendered the code.

Now let's see it working:

document.addEventListener("DOMContentLoaded", function(){
   var xmp = document.getElementById("XMlHtml"),
   code = document.getElementById("fonte"),
   tags = [],
   tab = "   ", // espaços de tabulação
   tabr = 0;

   nos(code);
   function nos(node){
       for (var i = 0; i < node.childNodes.length; i++){
         var child = node.childNodes[i];
         no(child); nos(child);
       }
   }
   
   function no(e){
      var html = e.nodeType != 3 ? e.outerHTML.match(/^<.+?>/)[0] : null;
      tags.push({ no: html, txt: e.nodeValue });
   }

   var nhtml = '';
   for(var x=0; x<tags.length; x++){
      var no = tags[x].no;
      if(x != 0 && tags[x-1].no) tabr++;
      if(!no) no = tags[x].txt;
      nhtml += tab.repeat(tabr)+no+"\n";
   }
   
   code.innerHTML = nhtml;
   xmp.innerHTML = code.innerHTML;
   
   window.exports = { 
      iniciar: function(){
         var linhas = xmp.querySelectorAll("li"),
         last = linhas[linhas.length-1],
         classe = Number(last.className.replace("L", "")),
         last_str = last.textContent,
         ltags = last_str.match(/<.+?>/g);
         
         for(var x=0; x<ltags.length; x++){

            tabr--;

            if(x == 0){
               if(tabr == 0) tabr++;
               last.querySelector("span").textContent = tab.repeat(tabr)+ltags[0];
            }else{
               classe = classe < 9 ? classe+1 : 0;
               if(tabr < 0) tabr = 0;
               var nli = document.createElement("li");
               nli.setAttribute("class", "L"+classe);
               var span = document.createElement("span");
               span.setAttribute("class", "tag");
               var tabula = document.createTextNode(tab.repeat(tabr));
               span.appendChild(tabula);
               span.appendChild(document.createTextNode(ltags[x]));
               nli.appendChild(span);
               xmp.querySelector("ol").appendChild(nli);
            }
         }
      }
   }
});
<script src="https://cdn.rawgit.com/google/code-prettify/master/loader/run_prettify.js?callback=iniciar"></script>
<?prettify lang=xml?>
<xmp class="prettyprint linenums:4" id="XMlHtml"></xmp>
<div id="fonte" style="display: none;"><properties><property key="expression">from CodigoOcorrencia couch in {[left] =&gt; DefaultIfEmpty()}</property><property key="EventId"><structure type=""><property key="Id">20500</property><property key="Name">Microsoft.EntityFrameworkCore.Query.QueryClientEvaluationWarning</property></structure></property><property key="SourceContext">Microsoft.EntityFrameworkCore.Query</property><property key="Scope"><sequence><item>Microsoft.EntityFrameworkCore.Query.RelationalQueryModelVisitor</item></sequence></property><property key="SetorErro">MVC</property></properties></div>

I put all the code inside the DOMContentLoaded event to restrict the scope of the variables and avoid possible conflicts with other variables you might be using in other code or script.

Also note that you can set the tab width by the variable var tab = " " , which in the case above, I put with 3 spaces.

Scroll to Top