17

I am having a bit of difficultly re the auto complete function in code mirror. What I am trying to do is two things (both which I am struggling with):

1) I want to enable auto complete for both HTML and JavaScript. Currently I can only get one working at a time using e.g.:

 CodeMirror.commands.autocomplete = function (cm) {
     CodeMirror.showHint(cm, CodeMirror.hint.html);
 };

How can I add the CodeMirror.hint.javascript to the list from the HTML one?

2) (Kind of more important) -- How can I add custom variables to the list of hints from HTML which area retrieved from an ajax call.....

i.e. I want to have the drop down show up with the current list of data from the html hints, but then add custom entries such as ##SomeCode1## and ##SomeCode2##

I have two problems here. First when I try to hard code the values in the 'html-hint.js' file the values all get appended with <... which is not what I want.

The second problem, kind of, is that I believe I have to write a new 'html-hint.js' file correct? I mean there is no way to pass anything in the 'options' parameter in the CodeMirror.hint.html above is there, to essentially merge two list.

I guest one and two are kind of the same come to think of it... Merging two lists of values for auto complete together.

I am guessing there is nothing already in the framework and I have to write a custom hint file, correct?

Any pointers would be appreciated. Sample code would be awesome.

Wyetro
  • 8,439
  • 9
  • 46
  • 64
Robin Rieger
  • 1,204
  • 1
  • 16
  • 37

3 Answers3

22

If you do not specify a hint function, the show-hint addon will take the hint helper function defined for the local mode at which the completion happens, so that will be CodeMirror.hint.javascript in JavaScript code, and CodeMirror.hint.html in HTML.

If you need to add your own completion logic, you can replace (or wrap) these functions by simply overwriting them with your own code.

Here is a crude example hack that always adds "bozo" to JavaScript completions:

var orig = CodeMirror.hint.javascript;
CodeMirror.hint.javascript = function(cm) {
  var inner = orig(cm) || {from: cm.getCursor(), to: cm.getCursor(), list: []};
  inner.list.push("bozo");
  return inner;
};
Wyetro
  • 8,439
  • 9
  • 46
  • 64
Marijn
  • 8,691
  • 2
  • 34
  • 37
  • 2
    Very helpful. Maybe you can extend your answer how to make real autocompletion like when I'm entered "bo" only "bozo" will be suggested and not f.e. "foobar" and that it does not add "bozo" behind "bo", but replaces "bo" with "bozo". Thanks :) – MonkeyMonkey Jan 29 '14 at 15:45
  • 1
    Marijn, the code above doesn't work as expected. The word 'bozo' is indeed added to the list, but as I type 'b-o-z...' it is not selected. On the contrary, if my page has a function named 'monkey,' as I type 'm-o-n...' that name is indeed selected. Also, the word 'bozo' is *always* displayed, not just when the user types a similar word. This is not the expected behavior for autocompletion. Are there any other ways to accomplish this? – shmim May 20 '16 at 18:54
  • 2
    The hint function is responsible for filtering. If it just unconditionally adds a word, as in my example, no filtering happens. But you can add your own logic there if you want. – Marijn May 21 '16 at 08:14
9

Thanks to @Marjin for brief explanation, but since it doesn't cover filtering and a lot of people needs it, here's what I've done in mongoclient by following Marjin's answer. And partially took help from here

p.s. Don't forget to change hint with yours, since I'm using javascript I've changed javascript hint.

CodeMirror.hint.javascript = function (editor) {
        var list = Session.get(Template.strSessionDistinctFields) || [];
        var cursor = editor.getCursor();
        var currentLine = editor.getLine(cursor.line);
        var start = cursor.ch;
        var end = start;
        while (end < currentLine.length && /[\w$]+/.test(currentLine.charAt(end))) ++end;
        while (start && /[\w$]+/.test(currentLine.charAt(start - 1))) --start;
        var curWord = start != end && currentLine.slice(start, end);
        var regex = new RegExp('^' + curWord, 'i');
        var result = {
            list: (!curWord ? list : list.filter(function (item) {
                return item.match(regex);
            })).sort(),
            from: CodeMirror.Pos(cursor.line, start),
            to: CodeMirror.Pos(cursor.line, end)
        };

        return result;
    };
Sercan Ozdemir
  • 4,641
  • 3
  • 34
  • 64
  • Is it possible to add custom hint list only to a certain CodeMirror instance? – peterremec Dec 05 '16 at 11:39
  • ofc, in that case you need to create your own mode for codemirror, http://stackoverflow.com/questions/24433552/writing-a-custom-mode-for-codemirror-for-use-in-brackets – Sercan Ozdemir Dec 05 '16 at 11:51
  • I'm not sure if we understand each other. I have two CodeMirror "sql mode" instances on a page with autocomplete function on. I need to add some custom keywords in addition to a built-in sql keywords. I don't understand why should I create a new mode. Besides that I'm looking for a way to dynamically add / remove keywords from that list – peterremec Dec 05 '16 at 12:46
  • Oh I see, unfortunately I'm not that good on this, maybe you can create a new question and people can answer, if you have a chance to communicate find @Marjin who's the creator of codemirror. – Sercan Ozdemir Dec 05 '16 at 13:33
  • @SercanOzdemir Please tell me how to search special character in autocomplete. i added some customize hint which is start from @ but in autocomplete they doesn't search from @ Please Suggest.. var regex = new RegExp('^' + curWord, 'i'); I think this regex will replace. –  Oct 17 '17 at 05:08
  • @Goku you actually answered your own question already, you need to escape special charachters in the `RegExp` constructor to make it work. – Sercan Ozdemir Oct 17 '17 at 07:04
  • @SercanOzdemir I don't know about regex i tried from my side but it doesn't give proper autocomplete and it doesn't find square bracket. var regex = curWord == "[" ? regex = new RegExp("\\[.*?\\]", "g") : regex = new RegExp('[@, ]*' + curWord, 'i'); I tried this but it didn't work –  Oct 17 '17 at 07:09
  • Neither do I, you need to have a look at here: https://www.regular-expressions.info/characters.html – Sercan Ozdemir Oct 17 '17 at 07:10
  • Please Suggest. Didn't found any solution for following hint " @input [ Square_Bracket ]" –  Oct 17 '17 at 07:16
  • 1
    no you need to escape per special charachter when they come from user input for example for the `@` sign you need to write it like `\@` before giving into `RegExp` constructor – Sercan Ozdemir Oct 17 '17 at 07:17
  • Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/156885/discussion-between-goku-and-sercan-ozdemir). –  Oct 17 '17 at 07:46
  • 1
    Thank you so much @SercanOzdemir, i have trying to understand this the whole day. – Tarounen Nov 17 '19 at 18:20
0

In case it helps someone, here's a version that combines parts from both @Marjin and Sercan. It uses the default hints, and adds our extras.

const hintWords=["trap.our()", "trap.hints()", "trap"]; // custom hints
const jsHinter = CodeMirror.hint.javascript; // copy default hinter for JavaScript
CodeMirror.hint.javascript = function (editor) {
    // Find the word fragment near cursor that needs auto-complete...
    const cursor = editor.getCursor();
    const currentLine = editor.getLine(cursor.line);
    let start = cursor.ch;
    let end = start;
    const rex=/[\w.]/; // a pattern to match any characters in our hint "words"
    // Our hints include function calls, e.g. "trap.getSource()"
    // so we search for word charcters (\w) and periods.
    // First (and optional), find end of current "word" at cursor...
    while (end < currentLine.length && rex.test(currentLine.charAt(end))) ++end;
    // Find beginning of current "word" at cursor...
    while (start && rex.test(currentLine.charAt(start - 1))) --start;
    // Grab the current word, if any...
    const curWord = start !== end && currentLine.slice(start, end);
    // Get the default results object from the JavaScript hinter...
    const dflt=jsHinter(editor);
    // If the default hinter didn't hint, create a blank result for now...
    const result = dflt || {list: []};
    // Set the start/end of the replacement range...
    result.to=CodeMirror.Pos(cursor.line, end);
    result.from=CodeMirror.Pos(cursor.line, start);
    // Add our custom hintWords to the list, if they start with the curWord...
    hintWords.forEach(h=>{if (h.startsWith(curWord)) result.list.push(h);});
    result.list.sort(); // sort the final list of hints
    return result;
};
Chris Janicki
  • 521
  • 6
  • 6