10
votes

I am using codemirror to allow the user to type anycode like css/html/js.

I need to enable if the user type something like in css mode

div {
 padding-
}

It should hint the user to chose the available options from the list like

div {
padding-top
padding-left
padding-right
padding-bottom
}

Something like sublime editor using codemirror. Please see the attached image for the demo of sublime auto hinting

enter image description here

Here is my code:

 <script src="codemirror-5.4/mode/javascript/javascript.js"></script>
  <script src="codemirror-5.4/mode/css/css.js"></script>
  <script src="codemirror-5.4/mode/htmlmixed/htmlmixed.js"></script>
  <script src="codemirror-5.4/addon/display/fullscreen.js"></script>
  <script src="codemirror-5.4/keymap/sublime.js"></script>
  <script src="codemirror-5.4/addon/hint/show-hint.js"></script>
  <script src="codemirror-5.4/addon/hint/css-hint.js"></script>
  <script src="codemirror-5.4/addon/hint/javascript.js"></script>

<h3>Editor</h3>
  <div class="control-group">
    <label class="control-label" for="textarea2">HTML</label>
    <div class="controls">
        <textarea class="code" name="code" id="codert" cols="40" rows="5" placeholder="Enter code here ..." style="width: 810px; height: 200px">
       </textarea>
        </div>
    </div>

    <div class="control-group">
        <label class="control-label" for="textarea3">CSS</label>
        <div class="controls">
          <textarea id="code" class="code" name="codeCSS"  cols="40" rows="5" placeholder="Enter code here ..." style="width: 810px; height: 200px">
        </textarea>
        </div>
    </div>
    <div class="control-group">
        <label class="control-label" for="textarea3">javascript</label>
        <div class="controls">
            <textarea id="codeJS" class="code" name="codeJS"  cols="40" rows="5" placeholder="Enter code here ..." style="width: 0px; height: 0px">
           </textarea>
        </div>
    </div>

JavaScript code for codemirror

<script>

   function loadCSS() {
    var $head = $("#preview").contents().find("head");                
    $head.html("<style>" + editor.getValue() + "</style>");
}; 

function loadJS() {
    var scriptTag = "<script>"+editorJS.getValue()+"<";
    scriptTag +=  "/script>";

    var previewFrame2 = document.getElementById('preview');
    var preview2 =  previewFrame2.contentDocument ||  previewFrame2.contentWindow.document;
    preview2.open();
    preview2.write(editor2.getValue()+scriptTag);
    preview2.close();

    loadCSS();
};

var delay;
// Initialize CodeMirror editor with a nice html5 canvas demo.

// css editor
var editor = CodeMirror.fromTextArea(document.getElementById('code'), {
    lineNumbers: true,
    styleActiveLine: true,
    matchBrackets: true,
    mode: "text/x-scss",
    keyMap: "sublime",
    theme: 'monokai',
    autoCloseTags: true,
    lineWrapping: true,
    extraKeys: {"Ctrl-Space": "autocomplete"}
});
editor.on("change", function() {
    clearTimeout(delay);

    delay = setTimeout(updatePreview, 0);
});

function updatePreview() {
    loadCSS();
}
setTimeout(updatePreview, 0);


var delay2;
// Initialize CodeMirror editor with a nice html5 canvas demo.
var editor2 = CodeMirror.fromTextArea(document.getElementById('codert'), {
    lineNumbers: true,
    styleActiveLine: true,
    matchBrackets: true,
    mode: "text/html",
    keyMap: "sublime",
    theme: 'monokai',
    autoCloseTags: true,
    lineWrapping: true,
    extraKeys: {"Ctrl-Space": "autocomplete"}
});
editor2.on("change", function() {
    clearTimeout(delay2);

    delay2 = setTimeout(updatePreview2, 0);
});

function updatePreview2() {
    var scriptTag = "<script>"+editorJS.getValue()+"<";
    scriptTag +=  "/script>";

    var previewFrame2 = document.getElementById('preview');
    var preview2 =  previewFrame2.contentDocument ||  previewFrame2.contentWindow.document;
    preview2.open();
    preview2.write(editor2.getValue()+scriptTag);
    preview2.close();

    loadCSS();
}
setTimeout(updatePreview2, 0);


var delayJS;
// Initialize CodeMirror editor with a nice html5 canvas demo.
var editorJS = CodeMirror.fromTextArea(document.getElementById('codeJS'), {
    lineNumbers: true,
    styleActiveLine: true,
    matchBrackets: true,
    mode: 'javascript',
    keyMap: "sublime",
    theme: 'monokai',
    autoCloseTags: true,
    lineWrapping: true,
    extraKeys: {"Ctrl-Space": "autocomplete"}
});
editorJS.on("change", function() {
    clearTimeout(delayJS);

    delayJS = setTimeout(updatePreviewJS, 0);
});

function updatePreviewJS() {
    loadJS();
}
setTimeout(updatePreviewJS, 0);
</script>
4

4 Answers

5
votes

You didn't post all the code so I could be wrong, but make sure to add the show-hint.css stylesheet to the page header.

<link rel="stylesheet" href="../addon/hint/show-hint.css">

Otherwise, the hints just don't show up and I for one assumed the autocomplete function was not working.

3
votes

from the code mirror website

function getCompletions(token, context) {
  var found = [], start = token.string;
  function maybeAdd(str) {
    if (str.indexOf(start) == 0) found.push(str);
  }
  function gatherCompletions(obj) {
    if (typeof obj == "string") forEach(stringProps, maybeAdd);
    else if (obj instanceof Array) forEach(arrayProps, maybeAdd);
    else if (obj instanceof Function) forEach(funcProps, maybeAdd);
    for (var name in obj) maybeAdd(name);
  }

  if (context) {
    // If this is a property, see if it belongs to some object we can
    // find in the current environment.
    var obj = context.pop(), base;
    if (obj.className == "js-variable")
      base = window[obj.string];
    else if (obj.className == "js-string")
      base = "";
    else if (obj.className == "js-atom")
      base = 1;
    while (base != null && context.length)
      base = base[context.pop().string];
    if (base != null) gatherCompletions(base);
  }
  else {
    // If not, just look in the window object and any local scope
    // (reading into JS mode internals to get at the local variables)
    for (var v = token.state.localVars; v; v = v.next) maybeAdd(v.name);
    gatherCompletions(window);
    forEach(keywords, maybeAdd);
  }
  return found;
}

you press ctrl+ space to enable code hinting.

3
votes

Weave: http://kodeweave.sourceforge.net/editor/#bc08cb08dee7609bbe7df11e8a55f27a

There are lots of different ways you can implement code hinting/autocomplete.

var arrows = [37, 38, 39, 40]

editor.on("keyup", function(cm, e) {
  if (arrows.indexOf(e.keyCode) < 0) {
    editor.execCommand("autocomplete")
  }
})

Of course Codemirror has official documentation for code hints/autocomplete.
XML completion
HTML completion
JavaScript completion

You can view the source for the javascript example to learn how to make your own hints too.

0
votes

This is how I enabled custom code hinting in my application's UI using CodeMirror 5

(it's basically a hodgepodge made out of several solutions I found online and from the source code at https://codemirror.net/demo/complete.html)

First of all I added CodeMirror and the code hint plugin

<script src="libs/codemirror-5/codemirror.min.js"></script>
<script src="libs/codemirror-5/plugins/show-hint.js"></script>

I overloaded (is that the right term?) the showHint command, placed it in a function (note that because CM calls this function, it will inject the instance context as a usable argument).

function getSnippets(codemirror) {

    let snippets = [
        {text: 'SELECT', displayName: 'select'},
        {text: 'UPDATE', displayName: 'update'},
        {text: 'CREATE', displayName: 'create'},
        // other snippets for hinting
    ]

    CodeMirror.showHint(codemirror, function () {

        let cursor = codemirror.getCursor();
        let line = codemirror.getLine(cursor.line);
        let start = cursor.ch, end = cursor.ch;
        // corrects ignoring trailing whitespaces removal
        while (start && /\w/.test(line.charAt(start - 1)))
                    --start;
        while (end < line.length && /\w/.test(line.charAt(end)))
                    ++end;
        const token = codemirror.getTokenAt(cursor);
        const currentWord = token.string;

        // reduce hint options if user has already entered something
        const list = snippets.filter(function (item) {
            return item.displayText.indexOf(currentWord) >= 0;
        });

        return {
            list: list.length ? list : snippets,
            from: CodeMirror.Pos(cursor.line, start),
            to: CodeMirror.Pos(cursor.line, end)
        };
    }, {completeSingle: true});
}

Then, inside the codemirror instance options I defined hint calling by pressing Ctrl+Space, this will call the getSnippets function I defined earlier

let codemirrorOptions = {
    mode: 'sql',
    extraKeys: {
        "Ctrl-Space": getSnippets
    },
    lineNumbers: false,
    lineWrapping: true
};