diff --git a/javascript/build/copy.js b/javascript/build/copy.js
index bac3d4925a3bb1fabe9c5a595fd3d3a295776b8c..c8d411c2ecd7cb3b45b92f3d2a62653b2648a546 100644
--- a/javascript/build/copy.js
+++ b/javascript/build/copy.js
@@ -91,7 +91,7 @@ const todos = [
         custom: [
             {
                 from: "javascript/src/Helper/codemirror/qfq.js",
-                to: target.js + "codemirror/"
+                to: target.js + "codemirror/qfq.js"
             }
         ]
     },{
diff --git a/javascript/build/terser.js b/javascript/build/terser.js
index 0ac9f153109b76ab857e4c7c09fb83fefa18fad9..8106f05927396774ac7686a6982c272beb1240aa 100644
--- a/javascript/build/terser.js
+++ b/javascript/build/terser.js
@@ -18,14 +18,6 @@ const todos = [
         name: "qfqValidator",
         input: "javascript/src/Plugins/validator.js",
         output: jsPath + "validator.min.js"
-    },{
-        name: "codemirror qfq",
-        input: "javascript/build/dist/codemirror/codemirror-qfq.js",
-        output: jsPath + "codemirror/codemirror-qfq.min.js",
-    },{
-        name: "codemirror BE qfq mode",
-        input: "javascript/src/Helper/codemirror/qfq.js",
-        output: jsPath + "codemirror/qfq.min.js",
     }
 ]
 
diff --git a/javascript/src/Helper/codemirror/addon-matchbrackets.js b/javascript/src/Helper/codemirror/addon-matchbrackets.js
deleted file mode 100644
index 971c41f22d83223f586d22611f8d21a96840cec3..0000000000000000000000000000000000000000
--- a/javascript/src/Helper/codemirror/addon-matchbrackets.js
+++ /dev/null
@@ -1,162 +0,0 @@
-// CodeMirror, copyright (c) by Marijn Haverbeke and others
-// Distributed under an MIT license: https://codemirror.net/5/LICENSE
-// This file was duplicated and only edited to include matching for <>
-// (couldn't figure out how to pass as config, if even possible)
-
-(function(mod) {
-    if (typeof exports == "object" && typeof module == "object") // CommonJS
-        mod(require("../../lib/codemirror"));
-    else if (typeof define == "function" && define.amd) // AMD
-        define(["../../lib/codemirror"], mod);
-    else // Plain browser env
-        mod(CodeMirror);
-})(function(CodeMirror) {
-    var ie_lt8 = /MSIE \d/.test(navigator.userAgent) &&
-        (document.documentMode == null || document.documentMode < 8);
-
-    var Pos = CodeMirror.Pos;
-
-    var matching = {"(": ")>", ")": "(<", "[": "]>", "]": "[<", "{": "}>", "}": "{<", "<": ">>", ">": "<<"};
-
-    function bracketRegex(config) {
-        return config && config.bracketRegex || /[(){}<>[\]]/
-    }
-
-    function findMatchingBracket(cm, where, config) {
-        var line = cm.getLineHandle(where.line), pos = where.ch - 1;
-        var afterCursor = config && config.afterCursor
-        if (afterCursor == null)
-            afterCursor = /(^| )cm-fat-cursor($| )/.test(cm.getWrapperElement().className)
-        var re = bracketRegex(config)
-
-        // A cursor is defined as between two characters, but in in vim command mode
-        // (i.e. not insert mode), the cursor is visually represented as a
-        // highlighted box on top of the 2nd character. Otherwise, we allow matches
-        // from before or after the cursor.
-        var match = (!afterCursor && pos >= 0 && re.test(line.text.charAt(pos)) && matching[line.text.charAt(pos)]) ||
-            re.test(line.text.charAt(pos + 1)) && matching[line.text.charAt(++pos)];
-        if (!match) return null;
-        var dir = match.charAt(1) == ">" ? 1 : -1;
-        if (config && config.strict && (dir > 0) != (pos == where.ch)) return null;
-        var style = cm.getTokenTypeAt(Pos(where.line, pos + 1));
-
-        var found = scanForBracket(cm, Pos(where.line, pos + (dir > 0 ? 1 : 0)), dir, style, config);
-        if (found == null) return null;
-        return {from: Pos(where.line, pos), to: found && found.pos,
-            match: found && found.ch == match.charAt(0), forward: dir > 0};
-    }
-
-    // bracketRegex is used to specify which type of bracket to scan
-    // should be a regexp, e.g. /[[\]]/
-    //
-    // Note: If "where" is on an open bracket, then this bracket is ignored.
-    //
-    // Returns false when no bracket was found, null when it reached
-    // maxScanLines and gave up
-    function scanForBracket(cm, where, dir, style, config) {
-        var maxScanLen = (config && config.maxScanLineLength) || 10000;
-        var maxScanLines = (config && config.maxScanLines) || 1000;
-
-        var stack = [];
-        var re = bracketRegex(config)
-        var lineEnd = dir > 0 ? Math.min(where.line + maxScanLines, cm.lastLine() + 1)
-            : Math.max(cm.firstLine() - 1, where.line - maxScanLines);
-        for (var lineNo = where.line; lineNo != lineEnd; lineNo += dir) {
-            var line = cm.getLine(lineNo);
-            if (!line) continue;
-            var pos = dir > 0 ? 0 : line.length - 1, end = dir > 0 ? line.length : -1;
-            if (line.length > maxScanLen) continue;
-            if (lineNo == where.line) pos = where.ch - (dir < 0 ? 1 : 0);
-            for (; pos != end; pos += dir) {
-                var ch = line.charAt(pos);
-                if (re.test(ch) && (style === undefined ||
-                    (cm.getTokenTypeAt(Pos(lineNo, pos + 1)) || "") == (style || ""))) {
-                    var match = matching[ch];
-                    if (match && (match.charAt(1) == ">") == (dir > 0)) stack.push(ch);
-                    else if (!stack.length) return {pos: Pos(lineNo, pos), ch: ch};
-                    else stack.pop();
-                }
-            }
-        }
-        return lineNo - dir == (dir > 0 ? cm.lastLine() : cm.firstLine()) ? false : null;
-    }
-
-    function matchBrackets(cm, autoclear, config) {
-        // Disable brace matching in long lines, since it'll cause hugely slow updates
-        var maxHighlightLen = cm.state.matchBrackets.maxHighlightLineLength || 1000,
-            highlightNonMatching = config && config.highlightNonMatching;
-        var marks = [], ranges = cm.listSelections();
-        for (var i = 0; i < ranges.length; i++) {
-            var match = ranges[i].empty() && findMatchingBracket(cm, ranges[i].head, config);
-            if (match && (match.match || highlightNonMatching !== false) && cm.getLine(match.from.line).length <= maxHighlightLen) {
-                var style = match.match ? "CodeMirror-matchingbracket" : "CodeMirror-nonmatchingbracket";
-                marks.push(cm.markText(match.from, Pos(match.from.line, match.from.ch + 1), {className: style}));
-                if (match.to && cm.getLine(match.to.line).length <= maxHighlightLen)
-                    marks.push(cm.markText(match.to, Pos(match.to.line, match.to.ch + 1), {className: style}));
-            }
-        }
-
-        if (marks.length) {
-            // Kludge to work around the IE bug from issue #1193, where text
-            // input stops going to the textarea whenever this fires.
-            if (ie_lt8 && cm.state.focused) cm.focus();
-
-            var clear = function() {
-                cm.operation(function() {
-                    for (var i = 0; i < marks.length; i++) marks[i].clear();
-                });
-            };
-            if (autoclear) setTimeout(clear, 800);
-            else return clear;
-        }
-    }
-
-    function doMatchBrackets(cm) {
-        cm.operation(function() {
-            if (cm.state.matchBrackets.currentlyHighlighted) {
-                cm.state.matchBrackets.currentlyHighlighted();
-                cm.state.matchBrackets.currentlyHighlighted = null;
-            }
-            cm.state.matchBrackets.currentlyHighlighted = matchBrackets(cm, false, cm.state.matchBrackets);
-        });
-    }
-
-    function clearHighlighted(cm) {
-        if (cm.state.matchBrackets && cm.state.matchBrackets.currentlyHighlighted) {
-            cm.state.matchBrackets.currentlyHighlighted();
-            cm.state.matchBrackets.currentlyHighlighted = null;
-        }
-    }
-
-    CodeMirror.defineOption("matchBrackets", false, function(cm, val, old) {
-        if (old && old != CodeMirror.Init) {
-            cm.off("cursorActivity", doMatchBrackets);
-            cm.off("focus", doMatchBrackets)
-            cm.off("blur", clearHighlighted)
-            clearHighlighted(cm);
-        }
-        if (val) {
-            cm.state.matchBrackets = typeof val == "object" ? val : {};
-            cm.on("cursorActivity", doMatchBrackets);
-            cm.on("focus", doMatchBrackets)
-            cm.on("blur", clearHighlighted)
-        }
-    });
-
-    CodeMirror.defineExtension("matchBrackets", function() {matchBrackets(this, true);});
-    CodeMirror.defineExtension("findMatchingBracket", function(pos, config, oldConfig){
-        // Backwards-compatibility kludge
-        if (oldConfig || typeof config == "boolean") {
-            if (!oldConfig) {
-                config = config ? {strict: true} : null
-            } else {
-                oldConfig.strict = config
-                config = oldConfig
-            }
-        }
-        return findMatchingBracket(this, pos, config)
-    });
-    CodeMirror.defineExtension("scanForBracket", function(pos, dir, style, config){
-        return scanForBracket(this, pos, dir, style, config);
-    });
-});
diff --git a/javascript/src/Helper/codemirror/qfq.js b/javascript/src/Helper/codemirror/qfq.js
index 7f0d83a1bd2ee62288cc9b835598759b4500a9f6..c1816c346b76c7a5d545f8eb92fa346208d280f0 100644
--- a/javascript/src/Helper/codemirror/qfq.js
+++ b/javascript/src/Helper/codemirror/qfq.js
@@ -624,7 +624,13 @@
                     list.push(h);
             });
 
-            return { list: list, from: from, to: to };
+            var onpick = function(completion) {
+                if (completion.tokenType === "qfq-level")
+                    cm.execCommand("indentAuto");
+            };
+            var completions = { list: list, from: from, to: to };
+            CodeMirror.on(completions, "pick", onpick);
+            return completions;
         }
     });
 
@@ -648,24 +654,30 @@
         });
     });
 
+    CodeMirror.defineOption("qfq-brackets", false, function(cm) {
+        // matchBrackets needs to be loaded first in order for this to work
+        // couldn't figure out how to pass a RegExp through JSON (which would be the proper way to configure this addon)
+        cm.state.matchBrackets.bracketRegex = /[(){}[\]<>]/;
+    });
+
     CodeMirror.defineOption("qfq-keymap", false, function(cm) {
-       cm.setOption("extraKeys", {
-           Tab: function(cm) {
-               if (cm.getSelections()[0].length) {
-                   // something selected: indent more
-                   cm.execCommand("indentMore");
-               } else {
-                   // insert one indentUnit worth of spaces
-                   var spaces = Array(cm.getOption("indentUnit") + 1).join(" ");
-                   cm.replaceSelection(spaces);
-               }
-           },
-           "Shift-Tab": function(cm) {
-               cm.execCommand("indentLess");
-           },
-           "Ctrl-Enter": function (cm) {
-               cm.execCommand("indentAuto");
-           }
-       })
+        cm.setOption("extraKeys", {
+            Tab: function(cm) {
+                if (cm.getSelections()[0].length) {
+                    // something selected: indent more
+                    cm.execCommand("indentMore");
+                } else {
+                    // insert one indentUnit worth of spaces
+                    var spaces = Array(cm.getOption("indentUnit") + 1).join(" ");
+                    cm.replaceSelection(spaces);
+                }
+            },
+            "Shift-Tab": function(cm) {
+                cm.execCommand("indentLess");
+            },
+            "Ctrl-Enter": function (cm) {
+                cm.execCommand("indentAuto");
+            }
+        })
     });
 });
\ No newline at end of file
diff --git a/package.json b/package.json
index 81d8ebd717faf4b2d32e867ba3ec687058bc7882..bc2cfce4b7c25b9aa327ae96b67e4fb400f88e2a 100644
--- a/package.json
+++ b/package.json
@@ -41,7 +41,7 @@
     "copy": "node javascript/build/copy.js",
     "echo": "echo \"$npm_package_config_js_dir\"",
     "concat": "concat -o javascript/build/dist/qfq.debug.js javascript/src/Core/QfqEvents.js javascript/src/Core/FormGroup.js javascript/src/*.js javascript/src/Helper/*.js javascript/src/Element/*.js",
-    "concat-cm": "concat -o javascript/build/dist/codemirror/codemirror-qfq.js node_modules/codemirror/lib/codemirror.js javascript/src/Helper/codemirror/qfq.js javascript/src/Helper/codemirror/addon-matchbrackets.js node_modules/codemirror/addon/search/match-highlighter.js node_modules/codemirror/addon/hint/show-hint.js node_modules/codemirror/addon/edit/closebrackets.js && concat -o less/dist/codemirror.css node_modules/codemirror/lib/codemirror.css node_modules/codemirror/theme/*.css node_modules/codemirror/addon/hint/show-hint.css less/codemirror-custom.css",
+    "concat-cm": "concat -o javascript/build/dist/codemirror/codemirror-qfq.js node_modules/codemirror/lib/codemirror.js node_modules/codemirror/addon/edit/matchbrackets.js node_modules/codemirror/addon/edit/closebrackets.js javascript/src/Helper/codemirror/qfq.js node_modules/codemirror/addon/search/match-highlighter.js node_modules/codemirror/addon/hint/show-hint.js && concat -o less/dist/codemirror.css node_modules/codemirror/lib/codemirror.css node_modules/codemirror/theme/*.css node_modules/codemirror/addon/hint/show-hint.css less/codemirror-custom.css",
     "terser": "node javascript/build/terser.js",
     "jshint": "jshint javascript/src --exclude javascript/src/Plugins",
     "less": "lessc -clean-css less/qfq-bs.css.less less/dist/qfq-bs.css && lessc -clean-css less/qfq-letter.css.less less/dist/qfq-letter.css && lessc -clean-css less/qfq-plain.css.less less/dist/qfq-plain.css && lessc -clean-css less/tablesorter-bootstrap.less less/dist/tablesorter-bootstrap.css",