Question

Is it possible to show hidden characters (like Carriage Return character) in Codemirror Text Editor, but I've not found any configuration reference about it in its documentation. Is it possible do this?

Was it helpful?

Solution 2

Carriage return is interpreted specially by CodeMirror (when on its own, it'll create a line break, when in front of a line feed, it'll be ignored), so in that case, no you can not.

But other non-printing characters (for example \b) will be visible as red dots by default, and you can adapt the relevant CSS class cm-invalidchar to customize their appearance.

OTHER TIPS

This could be done with help of overlays and predefined styles with whitespace and EOL symbol this way:

cm.addOverlay({
    name: 'invisibles',
    token:  function nextToken(stream) {
        var ret,
            spaces  = 0,
            peek    = stream.peek() === ' ';

        if (peek) {
            while (peek && spaces < Maximum) {
                ++spaces;

                stream.next();
                peek = stream.peek() === ' ';
            }

            ret = 'whitespace whitespace-' + spaces;
        } else {
            while (!stream.eol() && !peek) {
                stream.next();

                peek = stream.peek() === ' ';
            }

            ret = 'cm-eol';
        }

        return ret;
    }
});

You could use addon CodeMirror Show Invisibles for this purpose.

Yes you can do with overlay.js, and with any mode language. Only you need to define the overlay for each mode you want, but is the same javascript code for all modes.

window.onload = function() {
  CodeMirror.defineMode("javascript-hidden-chars", function(config, parserConfig) {
    var alterList = ["alter1", "alter2"];
    var spacesCount = 0;
    var hiddenCharsOverlay = {
      token: function(stream, state) {
        if (stream.match(/(?= )/)) {
          let alterSpace = spacesCount++ % alterList.length;
          stream.eat(/ /);
          return `space special-chars ${alterList[alterSpace]}`;
        }
        while (stream.next() != null && !stream.match(" ", false)) {}
        return null;
      }
    };
    return CodeMirror.overlayMode(
      CodeMirror.getMode(
        config,
        parserConfig.backdrop || "javascript"
      ),
      hiddenCharsOverlay
    );
  });
  var editorSimple = CodeMirror(document.querySelector("#simple-parser"), {
    lineNumbers: true,
    lineWrapping: true,
    foldGutter: true,
    gutters: ["CodeMirror-linenumbers", "CodeMirror-foldgutter"],
    mode: "javascript-hidden-chars",
    value: `// Demo code (the actual new parser character stream implementation)

function StringStream(string) {
  this.pos = 0;
  this.string = string;
}

StringStream.prototype = {
  done: function() {return this.pos >= this.string.length;},
  peek: function() {return this.string.charAt(this.pos);},
  next: function() {
    if (this.pos < this.string.length)
      return this.string.charAt(this.pos++);
  },
  eat: function(match) {
    var ch = this.string.charAt(this.pos);
    if (typeof match == "string") var ok = ch == match;
    else var ok = ch && match.test ? match.test(ch) : match(ch);
    if (ok) {this.pos++; return ch;}
  },
  eatWhile: function(match) {
    var start = this.pos;
    while (this.eat(match));
    if (this.pos > start) return this.string.slice(start, this.pos);
  },
  backUp: function(n) {this.pos -= n;},
  column: function() {return this.pos;},
  eatSpace: function() {
    var start = this.pos;
    while (/\s/.test(this.string.charAt(this.pos))) this.pos++;
    return this.pos - start;
  },
  match: function(pattern, consume, caseInsensitive) {
    if (typeof pattern == "string") {
      function cased(str) {return caseInsensitive ? str.toLowerCase() : str;}
      if (cased(this.string).indexOf(cased(pattern), this.pos) == this.pos) {
        if (consume !== false) this.pos += str.length;
        return true;
      }
    }
    else {
      var match = this.string.slice(this.pos).match(pattern);
      if (match && consume !== false) this.pos += match[0].length;
      return match;
    }
  }
};
`
  });
}
body {
  background: #4CB8C4;
  /* fallback for old browsers */
  background: -webkit-linear-gradient(to right, #3CD3AD, #4CB8C4);
  /* Chrome 10-25, Safari 5.1-6 */
  background: linear-gradient(to right, #3CD3AD, #4CB8C4);
  /* W3C, IE 10+/ Edge, Firefox 16+, Chrome 26+, Opera 12+, Safari 7+ */
}

.container-editor {
  font-size: 15px;
}

.CodeMirror-code div:not(:last-child) .CodeMirror-line:after {
  content: "↵";
  text-align: center;
  opacity: 0.5;
  z-index: 1;
  height: inherit;
  position: absolute;
  white-space: nowrap;
  pointer-events: none;
}

.cm-special-chars,
.cm-tab {
  position: relative;
  opacity: 0.5;
}

.cm-special-chars:after,
.cm-tab:after {
  opacity: 0.4;
  text-align: center;
  left: 0;
  top: 0;
  width: 100%;
  position: absolute;
  overflow: hidden;
  white-space: nowrap;
  pointer-events: none;
}

.cm-space {
  white-space: break-spaces;
  word-break: break-all;
}

.cm-space:after {
  content: '·';
  width: inherit;
}

.cm-tab {
  white-space: pre-wrap;
}

.cm-tab:after {
  content: '<---';
  direction: rtl;
}
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.63.1/codemirror.min.css" />
<link rel="stylesheet" href="https://codemirror.net/addon/fold/foldgutter.css" />

<div id="simple-parser" class="container-editor"></div>

<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/codemirror/5.63.1/codemirror.min.js"></script>
<script type="text/javascript" src="https://codemirror.net/mode/javascript/javascript.js"></script>
<script type="text/javascript" src="https://codemirror.net/addon/mode/overlay.js"></script>
<script type="text/javascript" src="https://codemirror.net/addon/fold/foldcode.js"></script>
<script type="text/javascript" src="https://codemirror.net/addon/fold/foldgutter.js"></script>
<script type="text/javascript" src="https://codemirror.net/addon/fold/comment-fold.js"></script>
<script type="text/javascript" src="https://codemirror.net/addon/fold/indent-fold.js"></script>
<script type="text/javascript" src="https://codemirror.net/addon/fold/brace-fold.js"></script>

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top