From a9616f8f4affd843032d139da841abcd4ffed61c Mon Sep 17 00:00:00 2001 From: Elias Villiger <elvill@ntl2.math.uzh.ch> Date: Fri, 12 Jan 2024 09:57:46 +0100 Subject: [PATCH] Integrate matchbrackets <> into qfq mode --- javascript/build/copy.js | 2 +- javascript/build/terser.js | 8 - .../Helper/codemirror/addon-matchbrackets.js | 162 ------------------ javascript/src/Helper/codemirror/qfq.js | 50 ++++-- package.json | 2 +- 5 files changed, 33 insertions(+), 191 deletions(-) delete mode 100644 javascript/src/Helper/codemirror/addon-matchbrackets.js diff --git a/javascript/build/copy.js b/javascript/build/copy.js index bac3d4925..c8d411c2e 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 0ac9f1531..8106f0592 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 971c41f22..000000000 --- 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 7f0d83a1b..c1816c346 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 81d8ebd71..bc2cfce4b 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", -- GitLab