'Codemirror Auto Format after setValue

http://liveweave.com/UxEJ0s

I'm using Codemirror for my app.

I noticed if I select all the text and press SHIFT+Tab it will auto align my code making it easier to read.

Here's an example of what my app currently renders:

<ul>
<li>
<font color="#f90000">
  Apples
</font>
</li>
<li>
<font color="#ff9a3d">
  Oranges
</font>
</li>
</ul>

Here's what I'm trying to get it to render:

<ul>
  <li>
    <font color="#f90000">
      Apples
    </font>
  </li>
  <li>
    <font color="#ff9a3d">
      Oranges
    </font>
  </li>
</ul>

Edit

Does anyone know if there's a way to do this without selecting the whole code manually in Codemirror?

Why? I have Codemirror running in my background of my app all code that's added is added dynamically, but when I save the final code it looks like above.



Solution 1:[1]

Ever since Codemirror has removed support for autoFormatRange() it's not worth the trouble to use it for formatting text. I use js-beautify instead.

var beautify_js = require('js-beautify').js_beautify
var beautify_html = require('js-beautify').html

var formattedJSON = beautify_js(jsonText, { indent_size: 2 });
var formattedXML = beautify_html(xmlText, { indent_size: 2 });

Solution 2:[2]

You can use the following code to achieve what you want :

function format() {
    var totalLines = editor.lineCount();  
    editor.autoFormatRange({line:0, ch:0}, {line:totalLines});
}

Bind this function with your events, and it will auto-format the code.

Solution 3:[3]

By using codemirror formatting add-on you can achieve your requirement

JSFiddle Demo

 var editor = CodeMirror.fromTextArea(document.getElementById("code"), {
        lineNumbers: true,
        mode: "htmlmixed",
        extraKeys:{"Shift-Tab":autoFormatSelection}
      });


      function getSelectedRange() {
        return { from: editor.getCursor(true), to: editor.getCursor(false) };
      }

      function autoFormatSelection() {
        var range = getSelectedRange();
        editor.autoFormatRange(range.from, range.to);
      }

Source Link

http://codemirror.net/2/demo/formatting.html

Solution 4:[4]

Here is the original addon including a small update to let it work with CodeMirror V3 :

CodeMirror.extendMode("css", {
    commentStart: "/*",
    commentEnd: "*/",
    newlineAfterToken: function(type, content) {
        return /^[;{}]$/.test(content);
    }
});

CodeMirror.extendMode("javascript", {
    commentStart: "/*",
    commentEnd: "*/",    
    newlineAfterToken: function(type, content, textAfter, state) {
        if (this.jsonMode) {
            return /^[\[,{]$/.test(content) || /^}/.test(textAfter);
        } else {
            if (content == ";" && state.lexical && state.lexical.type == ")") return false;
        return /^[;{}]$/.test(content) && !/^;/.test(textAfter);
        }
    }
});

CodeMirror.extendMode("xml", {
    commentStart: "<!--",
    commentEnd: "-->",
    newlineAfterToken: function(type, content, textAfter) {    
        return ( type == "tag" && />$/.test(content) || /^</.test(textAfter) ) || (  type == "tag bracket" && />$/.test(content) );
    }
});


CodeMirror.defineExtension("commentRange", function (isComment, from, to) {
    var cm = this, curMode = CodeMirror.innerMode(cm.getMode(), cm.getTokenAt(from).state).mode;
    cm.operation(function() {
        if (isComment) { // Comment range
            cm.replaceRange(curMode.commentEnd, to);
            cm.replaceRange(curMode.commentStart, from);
        if (from.line == to.line && from.ch == to.ch) // An empty comment inserted - put cursor inside
            cm.setCursor(from.line, from.ch + curMode.commentStart.length);
        } else { // Uncomment range
            var selText = cm.getRange(from, to);
            var startIndex = selText.indexOf(curMode.commentStart);
            var endIndex = selText.lastIndexOf(curMode.commentEnd);
            if (startIndex > -1 && endIndex > -1 && endIndex > startIndex) {
                // Take string till comment start
                selText = selText.substr(0, startIndex)
                // From comment start till comment end
                + selText.substring(startIndex + curMode.commentStart.length, endIndex)
                // From comment end till string end
                + selText.substr(endIndex + curMode.commentEnd.length);
            }
            cm.replaceRange(selText, from, to);
        }
    });
});


CodeMirror.defineExtension("autoIndentRange", function (from, to) {
    var cmInstance = this;
    this.operation(function () {
        for (var i = from.line; i <= to.line; i++) {
            cmInstance.indentLine(i, "smart");
        }
    });
});


CodeMirror.defineExtension("autoFormatRange", function (from, to) {
    var cm = this;
    var outer = cm.getMode(), text = cm.getRange(from, to).split("\n");        
    var state = CodeMirror.copyState(outer, cm.getTokenAt(from).state);
    var tabSize = cm.getOption("tabSize");

    var out = "", lines = 0, atSol = from.ch == 0;
    function newline() {
      out += "\n";
      atSol = true;
      ++lines;
    }

    for (var i = 0; i < text.length; ++i) {
      var stream = new CodeMirror.StringStream(text[i], tabSize);      
      while (!stream.eol()) {
        var inner = CodeMirror.innerMode(outer, state);   
        var style = outer.token(stream, state), cur = stream.current();
        stream.start = stream.pos;
        if (!atSol || /\S/.test(cur)) {
          out += cur;
          atSol = false;
        }

        if (!atSol && inner.mode.newlineAfterToken &&
            inner.mode.newlineAfterToken(style, cur, stream.string.slice(stream.pos) || text[i+1] || "", inner.state))
          newline();        
      }
      if (!stream.pos && outer.blankLine) outer.blankLine(state);
      if (!atSol)newline();
    }

    cm.operation(function () {
      cm.replaceRange(out, from, to);
      for (var cur = from.line + 1, end = from.line + lines; cur <= end; ++cur)
        cm.indentLine(cur, "smart");
      cm.setSelection(from, cm.getCursor(false));
    });
});

Solution 5:[5]

A bit late, but maybe this can help someone who faces the same issue

autoFormatRange is not available anymore, however it is possible to indent the editor's value line by line, and no need to select the content

editor.eachLine(line => {
   editor.indentLine(editor.getLineNumber(line), "smart");
})

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 RajV
Solution 2 djadmin
Solution 3
Solution 4 iole
Solution 5 Alaa Eddine Cherbib