28

I'm working on trying to add a custom autocomplete, that I want to trigger whenever the user is typing (configurable of course). I've found a couple examples of autocomplete for codemirror:

http://codemirror.net/demo/complete.html and http://codemirror.net/demo/xmlcomplete.html

But both of these trigger on specific keys (Control-Space for one and '<' for the other) and both use the extraKeys functionality to process the events, but I want to trigger from any key. I have tried the following:

        var editor = CodeMirror.fromTextArea(document.getElementById("code"),
        {
             lineNumbers: true,
             mode: "text/x-mysql",
             fixedGutter: true,
             gutter: true,
//           extraKeys: {"'.'": "autocomplete"}
             keyup: function(e)
             {
                console.log('testing');
             },
             onkeyup: function(e)
             {
                console.log('testing2');
             }
        });

But have had no luck. Any suggestions on how I could trigger from any keyup events?

Kyle
  • 17,317
  • 32
  • 140
  • 246

11 Answers11

36

For version 5.7 neither of the previously proposed solutions work fine for me (and I think they have bugs even for earlier versions). My solution:

    myCodeMirror.on("keyup", function (cm, event) {
        if (!cm.state.completionActive && /*Enables keyboard navigation in autocomplete list*/
            event.keyCode != 13) {        /*Enter - do not open autocomplete list just after item has been selected in it*/ 
            CodeMirror.commands.autocomplete(cm, null, {completeSingle: false});
        }
    });

How it works:

This opens autocomplete popup only if it is not opened yet (otherwise keyboard-navigation would have caused reopening the popup with 1st item selected again).

When you click Enter you want popup to close so this is special case of a character which shouldn't trigger autocompletion (you may consider a case when you want to show antocompletion for empty line though).

Then last fix is setting completeSingle: false which prevents case when you are typing some word and in the middle it is automatically completed and you continue typing by reflex. So user will always need to select the intended string from popup (even if it's single option).

Sasha
  • 8,537
  • 4
  • 49
  • 76
  • 1
    This works except that stray bracket at the end of the comment breaks the code: /*Enter - do not open autocomplete list just after item has been selected in it*/) <<< this bracket – KB. Mar 01 '16 at 22:04
  • what does the 'null' ¿? – Alberto Acuña Mar 16 '16 at 11:26
  • 8
    @AlbertoAcuña Instead of `CodeMirror.commands.autocomplete(cm, null, {completeSingle: false});` (which according to a comment in the code is now the older interface kept for backward-compatibility), you can use `cm.showHint({completeSingle: false});` – ShreevatsaR Jul 15 '17 at 08:30
14

The most IntelliSense-like behavior can be achieved by this:

var ExcludedIntelliSenseTriggerKeys =
{
    "8": "backspace",
    "9": "tab",
    "13": "enter",
    "16": "shift",
    "17": "ctrl",
    "18": "alt",
    "19": "pause",
    "20": "capslock",
    "27": "escape",
    "33": "pageup",
    "34": "pagedown",
    "35": "end",
    "36": "home",
    "37": "left",
    "38": "up",
    "39": "right",
    "40": "down",
    "45": "insert",
    "46": "delete",
    "91": "left window key",
    "92": "right window key",
    "93": "select",
    "107": "add",
    "109": "subtract",
    "110": "decimal point",
    "111": "divide",
    "112": "f1",
    "113": "f2",
    "114": "f3",
    "115": "f4",
    "116": "f5",
    "117": "f6",
    "118": "f7",
    "119": "f8",
    "120": "f9",
    "121": "f10",
    "122": "f11",
    "123": "f12",
    "144": "numlock",
    "145": "scrolllock",
    "186": "semicolon",
    "187": "equalsign",
    "188": "comma",
    "189": "dash",
    "190": "period",
    "191": "slash",
    "192": "graveaccent",
    "220": "backslash",
    "222": "quote"
}

EditorInstance.on("keyup", function(editor, event)
{
    var __Cursor = editor.getDoc().getCursor();
    var __Token = editor.getTokenAt(__Cursor);

    if (!editor.state.completionActive &&
        !ExcludedIntelliSenseTriggerKeys[(event.keyCode || event.which).toString()] &&
        (__Token.type == "tag" || __Token.string == " " || __Token.string == "<" || __Token.string == "/"))
    {
        CodeMirror.commands.autocomplete(editor, null, { completeSingle: false });
    }
});
Tobias Punke
  • 730
  • 1
  • 7
  • 15
  • Can you explain a little bit more? In particular, the `__Token` conditions, which seem to work with xml(?) – Christian Mar 03 '16 at 21:41
  • This array contains key codes, which should not trigger an IntelliSense dropdown, ie. the function keys, shift or control. if the event from CodeMirror is raised, the pressed key will be checked against this array and the handler will only continue if it's not in there. This is just to avoid unwanted pop out of the menu (which I found very annoying). – Tobias Punke Mar 04 '16 at 16:11
  • 2
    I added "32": "space" and removed "190": "period" to be even more like Visual Studio IntelliSense like. – Jan Jun 28 '16 at 22:15
  • Works really well - http://kodeweave.sourceforge.net/editor/#d956c96bdee0cdd1ce9193aee78353ac – Michael Schwartz Jul 04 '16 at 00:33
  • The `exclude keys` are working great! But I don't understand the token conditions. That's only limiting the autocomplete again to a very small subset. I've removed that part from the condition and now it's working great. Did you mean to negate it? – s.meijer May 27 '20 at 08:30
  • never mind, I understand now. It's for characters that use modifier keys. I've replaced it with `&& !(token.string === '(' || token.string === ')')`. – s.meijer May 27 '20 at 09:18
13

To also display the autocomplete widget:

onKeyEvent: function (e, s) {
    if (s.type == "keyup") {
        CodeMirror.showHint(e);
    }
}
Buh Buh
  • 7,443
  • 1
  • 34
  • 61
11
editor.on("inputRead",function(cm,changeObj){
   // hinting logic
})

As far I've seen, "inputRead" is the best event to show "auto completions" in "codemirror". The only drawback is that you can't show hints on backspace or delete.

Ciaran Donoghue
  • 800
  • 3
  • 22
  • 46
Damodharan J
  • 350
  • 2
  • 10
  • 2
    Can you elaborate on why it's the best choice? Also, if you could show some example hinting logic that you successfully implemented, that would help others. – shmim May 20 '16 at 20:12
  • 2
    "keyup"/"keydown" event captures up/down/right/left keys. So "inputRead" is the best way to capture the hinting logic. As far the hinting logic is concerned, it depends on the language for which you want to implement it. For javascript you can look into "tern.js". For building a hinting logic, you need to look into tokenizers, parsers and semantics of the target language before proceeding. – Damodharan J Nov 16 '16 at 14:01
  • Thanks, I wasn't even aware of this event. For the hinting logic, I simply used `cm.showHint({completeSingle: false})`. – Nickkk May 26 '17 at 13:08
5

Use this function to autocomplete codeMirror without CTRL + Space.

set completeSingle to false in the show-hint.js

editor.on("inputRead", function(instance) {
    if (instance.state.completionActive) {
            return;
    }
    var cur = instance.getCursor();
    var token = instance.getTokenAt(cur);
    if (token.type && token.type != "comment") {
            CodeMirror.commands.autocomplete(instance);
    }
});
SWAPNIL KUWAR
  • 404
  • 4
  • 11
4

Let me share a full example that contains autocomplete(for hive sql) after any keyup:

Include scripts and styles:

<link rel="stylesheet" href="/static/codemirror/lib/codemirror.css">
<link rel="stylesheet" href="/static/codemirror/theme/material.css">
<link rel="stylesheet" href="/static/codemirror/addon/hint/show-hint.css" />

<script type="text/javascript" src="/static/codemirror/lib/CodeMirror.js"></script>
<script type="text/javascript" src="/static/codemirror/mode/sql/sql.js"></script>
<script type="text/javascript" src="/static/codemirror/addon/hint/show-hint.js"></script>
<script type="text/javascript" src="/static/codemirror/addon/hint/sql-hint.js"></script>

Html :

<textarea id="code" name="code" rows="4" placeholder="" value=""></textarea>

Script :

<script>

    $(function () {
        initSqlEditor();
        initAutoComplete();
    });

    // init sql editor
    function initSqlEditor() {

        var editor = CodeMirror.fromTextArea(document.getElementById('code'), {
            autofocus: true,
            extraKeys: {
                "Tab": "autocomplete"
            },
            hint: CodeMirror.hint.sql,
            lineNumbers: true,
            mode: 'text/x-hive',
            lineWrapping: true,
            theme: 'material',
        });

        editor.on('keyup', function(editor, event){
            // type code and show autocomplete hint in the meanwhile
            CodeMirror.commands.autocomplete(editor);
        });
    }

    /**
     * Init autocomplete for table name and column names in table.
     */
    function initAutoComplete() {

        CodeMirror.commands.autocomplete = function (cmeditor) {

            CodeMirror.showHint(cmeditor, CodeMirror.hint.sql, {

                // "completeSingle: false" prevents case when you are typing some word
                // and in the middle it is automatically completed and you continue typing by reflex.
                // So user will always need to select the intended string
                // from popup (even if it's single option). (copy from @Oleksandr Pshenychnyy)
                completeSingle: false,

                // there are 2 ways to autocomplete field name:
                // (1) table_name.field_name (2) field_name
                // Put field name in table names to autocomplete directly
                // no need to type table name first.
                tables: {
                    "table1": ["col_A", "col_B", "col_C"],
                    "table2": ["other_columns1", "other_columns2"],
                    "col_A": [],
                    "col_B": [],
                    "col_C": [],
                    "other_columns1": [],
                    "other_columns2": [],
                }
            });
        }
    }

</script>
Gary Gauh
  • 4,984
  • 5
  • 30
  • 43
4

I think everyone has their own use cases. I also had to club parts from different answers to make something which is best for my case.

According to me, I want to show suggestions only on alphabets, numbers, and (.) with an exception of ctrl key pressed. because sometimes I copy or paste something, so that should not open suggestions. 46 ascii is for (.) which I've included with numbers.

activeEditor.on("keydown", function (cm, event) {
  if (
    !(event.ctrlKey) &&
    (event.keyCode >= 65 && event.keyCode <= 90) || 
    (event.keyCode >= 97 && event.keyCode <= 122) || 
    (event.keyCode >= 46 && event.keyCode <= 57)
  ) {
    CodeMirror.commands.autocomplete(cm, null, {completeSingle: false});
  }
});

Do remeber to include 3 things -

  1. js and css of show hint - <link rel="stylesheet" href="codemirror/addon/hint/show-hint.css"> <script src="codemirror/addon/hint/show-hint.js"></script>

  2. script for language you want the hint for - for ex - javascript <script src="codemirror/addon/hint/javascript-hint.js"></script>

  3. include this line while initializing your code editor. I've used javascript hint. hint: CodeMirror.hint.javascript

Ronn Wilder
  • 1,228
  • 9
  • 13
3
editor.on('keyup', function(){
    CodeMirror.commands.autocomplete(editor);
});

it may works

Yuting Woo
  • 31
  • 2
  • This does not provide an answer to the question. To critique or request clarification from an author, leave a comment below their post - you can always comment on your own posts, and once you have sufficient [reputation](http://stackoverflow.com/help/whats-reputation) you will be able to [comment on any post](http://stackoverflow.com/help/privileges/comment). – Marvin Emil Brach Mar 19 '15 at 08:12
  • 3
    It is an answer to this question! Add this code below your code, every time when your key up, it will fire the autocomplete event. – Yuting Woo Mar 24 '15 at 03:04
  • If you press an arrow key to move down the hint options, it will reset and get stuck on the top option, with this code. – kintsukuroi Sep 08 '18 at 03:28
3

changed Oleksandr Pshenychnyy's answer a little bit(see here), Answering as I can't add comments yet

The code below only allows autocomplete to come up when a letter key is pressed (probably what you want instead on any key)

editor.on("keyup", function (cm, event) {
if (!cm.state.completionActive &&   /*Enables keyboard navigation in autocomplete list*/
     event.keyCode > 64 && event.keyCode < 91){// only when a letter key is pressed
         CodeMirror.commands.autocomplete(cm, null, {completeSingle: false});
    }
});

(this should work logically, but could some please comment if this works or not !)

Aditya Shankar
  • 702
  • 6
  • 12
2

NOTE: This answer does not work on recent versions of CodeMirror.

onKeyEvent: function(e , s){
                if (s.type == "keyup")
                {
                    console.log("test");   
                }
            }
Sridhar Ratnakumar
  • 81,433
  • 63
  • 146
  • 187
aljordan82
  • 1,710
  • 1
  • 16
  • 22
2

Change event is better option for this situation

editor.on('change', (cm, event) => {
         editor.execCommand('autocomplete');
        });
  • 1
    Indeed, "execCommand('autocomplete')" is what I needed to use with Angular and ngx-codemirror, thanks for the tips! – Pierre Jan 09 '21 at 02:26