Permalink
Browse files

Move matchBrackets out of the core

Add CodeMirror.defineOption function for defining new options.

Add findMatchingBracket to the methods exported by this extension.

Closes #746
  • Loading branch information...
marijnh committed Aug 28, 2012
1 parent 753e838 commit 3721870ef320c4621a2998a12f424a19fb77ba87
View
@@ -32,7 +32,6 @@ <h1>CodeMirror: Mode-Changing demo</h1>
var editor = CodeMirror.fromTextArea(document.getElementById("code"), {
mode: "scheme",
lineNumbers: true,
- matchBrackets: true,
tabMode: "indent"
});
editor.connect("change", function() {
View
@@ -119,6 +119,7 @@ <h1><span class="logo-braces">{ }</span> <a href="http://codemirror.net/">CodeMi
<option value="http://codemirror.net/lib/util/dialog.js">dialog.js</option>
<option value="http://codemirror.net/lib/util/search.js">search.js</option>
<option value="http://codemirror.net/lib/util/searchcursor.js">searchcursor.js</option>
+ <option value="http://codemirror.net/lib/util/matchbrackets.js">matchbrackets.js</option>
<option value="http://codemirror.net/lib/util/formatting.js">formatting.js</option>
<option value="http://codemirror.net/lib/util/match-highlighter.js">match-highlighter.js</option>
<option value="http://codemirror.net/lib/util/closetag.js">closetag.js</option>
View
@@ -211,10 +211,6 @@ <h2 id="config">Configuration</h2>
simply <code>true</code>), focusing of the editor is also
disallowed.</dd>
- <dt id="option_matchBrackets"><code>matchBrackets (boolean)</code></dt>
- <dd>Determines whether brackets are matched whenever the cursor
- is moved next to a bracket.</dd>
-
<dt id="option_cursorBlinkRate"><code>cursorBlinkRate (number)</code></dt>
<dd>Half-period in milliseconds used for cursor blinking. The default blink
rate is 530ms.</dd>
@@ -740,9 +736,6 @@ <h2 id="api">Programming API</h2>
widget again, simply use DOM methods (move it somewhere else, or
call <code>removeChild</code> on its parent).</dd>
- <dt id="matchBrackets"><code>matchBrackets()</code></dt>
- <dd>Force matching-bracket-highlighting to happen.</dd>
-
<dt id="lineCount"><code>lineCount() → number</code></dt>
<dd>Get the number of lines in the editor.</dd>
@@ -867,6 +860,13 @@ <h2 id="api">Programming API</h2>
will cause the given value (usually a method) to be added to all
CodeMirror instances created from then on.</p>
+ <p id="defineOption">Similarly, <code>CodeMirror.defineOption(name,
+ default, updateFunc)</code> can be used to define new options for
+ CodeMirror. The <code>updateFunc</code> will be called with the
+ editor instance and the new value when an editor is initialized,
+ and whenever the option is modified
+ through <a href="#setOption"><code>setOption</code></a>.</p>
+
<h2 id="addons">Add-ons</h2>
<p>The <code>lib/util</code> directory in the distribution
@@ -917,6 +917,14 @@ <h2 id="addons">Add-ons</h2>
on <code>searchcursor.js</code>, and will make use
of <a href="#util_dialog"><code>openDialog</code></a> when
available to make prompting for search queries less ugly.</dd>
+ <dt id="util_matchbrackets"><a href="../lib/util/matchbrackets.js"><code>matchbrackets.js</code></a></dt>
+ <dd>Defines an option <code>matchBrackets</code> which, when set
+ to true, causes matching brackets to be highlighted whenever the
+ cursor is next to them. It also adds a
+ method <code>matchBrackets</code> that forces this to happen
+ once, and a method <code>findMatchingBracket</code> that can be
+ used to run the bracket-finding algorithm that this uses
+ internally.</dd>
<dt id="util_foldcode"><a href="../lib/util/foldcode.js"><code>foldcode.js</code></a></dt>
<dd>Helps with code folding.
See <a href="../demo/folding.html">the demo</a> for an example.
View
@@ -21,19 +21,21 @@ window.CodeMirror = (function() {
replaceSelection: operation(replaceSelection),
focus: function(){window.focus(); focusInput(); onFocus(); fastPoll();},
setOption: function(option, value) {
- var oldVal = options[option];
+ if (options[option] == value) return;
options[option] = value;
if (option == "mode" || option == "indentUnit") loadMode();
else if (option == "readOnly" && value == "nocursor") {onBlur(); input.blur();}
else if (option == "readOnly" && !value) {resetInput(true);}
else if (option == "theme") themeChanged();
- else if (option == "lineWrapping" && oldVal != value) operation(wrappingChanged)();
+ else if (option == "lineWrapping") operation(wrappingChanged)();
else if (option == "tabSize") updateDisplay(true);
else if (option == "keyMap") keyMapChanged();
else if (option == "gutters" || option == "lineNumbers") setGuttersForLineNumbers();
if (option == "lineNumbers" || option == "gutters" || option == "firstLineNumber" ||
option == "theme" || option == "lineNumberFormatter")
guttersChanged();
+ if (optionHandlers.hasOwnProperty(option))
+ optionHandlers[option](instance, value);
},
getOption: function(option) {return options[option];},
undo: operation(undo),
@@ -57,7 +59,6 @@ window.CodeMirror = (function() {
history.time = 0;
return {done: history.done.concat([]), undone: history.undone.concat([])};
},
- matchBrackets: operation(function(){matchBrackets(true);}),
getTokenAt: operation(function(pos) {
pos = clipPos(pos);
return getLine(pos.line).getTokenAt(mode, getStateBefore(pos.line), options.tabSize, pos.ch);
@@ -2012,46 +2013,6 @@ window.CodeMirror = (function() {
}, options.cursorBlinkRate);
}
- var matching = {"(": ")>", ")": "(<", "[": "]>", "]": "[<", "{": "}>", "}": "{<"};
- function matchBrackets(autoclear) {
- var head = sel.inverted ? sel.from : sel.to, line = getLine(head.line), pos = head.ch - 1;
- var match = (pos >= 0 && matching[line.text.charAt(pos)]) || matching[line.text.charAt(++pos)];
- if (!match) return;
- var ch = match.charAt(0), forward = match.charAt(1) == ">", d = forward ? 1 : -1, st = line.styles;
- for (var off = pos + 1, i = 0, e = st.length; i < e; i+=2)
- if ((off -= st[i].length) <= 0) {var style = st[i+1]; break;}
-
- var stack = [line.text.charAt(pos)], re = /[(){}[\]]/;
- function scan(line, from, to) {
- if (!line.text) return;
- var st = line.styles, pos = forward ? 0 : line.text.length - 1, cur;
- for (var i = forward ? 0 : st.length - 2, e = forward ? st.length : -2; i != e; i += 2*d) {
- var text = st[i];
- if (st[i+1] != style) {pos += d * text.length; continue;}
- for (var j = forward ? 0 : text.length - 1, te = forward ? text.length : -1; j != te; j += d, pos+=d) {
- if (pos >= from && pos < to && re.test(cur = text.charAt(j))) {
- var match = matching[cur];
- if (match.charAt(1) == ">" == forward) stack.push(cur);
- else if (stack.pop() != match.charAt(0)) return {pos: pos, match: false};
- else if (!stack.length) return {pos: pos, match: true};
- }
- }
- }
- }
- for (var i = head.line, e = forward ? Math.min(i + 100, doc.size) : Math.max(-1, i - 100); i != e; i+=d) {
- var line = getLine(i), first = i == head.line;
- var found = scan(line, first && forward ? pos + 1 : 0, first && !forward ? pos : line.text.length);
- if (found) break;
- }
- if (!found) found = {pos: null, match: false};
- var style = found.match ? "CodeMirror-matchingbracket" : "CodeMirror-nonmatchingbracket";
- var one = markText({line: head.line, ch: pos}, {line: head.line, ch: pos+1}, style),
- two = found.pos != null && markText({line: i, ch: found.pos}, {line: i, ch: found.pos + 1}, style);
- var clear = operation(function(){one.clear(); two && two.clear();});
- if (autoclear) setTimeout(clear, 800);
- else bracketHighlighted = clear;
- }
-
// Finds the line to start with when starting a parse. Tries to
// find a line with a stateAfter, so that it can start with a
// valid state. If that fails, it returns the line with the
@@ -2168,11 +2129,6 @@ window.CodeMirror = (function() {
(updateInput === true || (updateInput !== false && selectionChanged)))
resetInput(userSelChange);
- if (selectionChanged && options.matchBrackets)
- setTimeout(operation(function() {
- if (bracketHighlighted) {bracketHighlighted(); bracketHighlighted = null;}
- if (posEq(sel.from, sel.to)) matchBrackets(false);
- }), 20);
var sc = selectionChanged, cbs = delayedCallbacks; // these can be reset by callbacks
delayedCallbacks = null;
if (textChanged)
@@ -2200,6 +2156,9 @@ window.CodeMirror = (function() {
if (extensions.propertyIsEnumerable(ext) &&
!instance.propertyIsEnumerable(ext))
instance[ext] = extensions[ext];
+ for (var opt in optionHandlers)
+ if (optionHandlers.propertyIsEnumerable(opt))
+ optionHandlers[opt](instance, options[opt]);
return instance;
} // (end of function CodeMirror)
@@ -2225,7 +2184,6 @@ window.CodeMirror = (function() {
firstLineNumber: 1,
readOnly: false,
dragDrop: true,
- matchBrackets: false,
cursorBlinkRate: 530,
workTime: 100,
workDelay: 200,
@@ -2284,6 +2242,11 @@ window.CodeMirror = (function() {
CodeMirror.defineExtension = function(name, func) {
extensions[name] = func;
};
+ var optionHandlers = CodeMirror.optionHandlers = {};
+ CodeMirror.defineOption = function(name, deflt, handler) {
+ CodeMirror.defaults[name] = deflt;
+ optionHandlers[name] = handler;
+ };
var commands = CodeMirror.commands = {
selectAll: function(cm) {cm.setSelection({line: 0, ch: 0}, {line: cm.lineCount() - 1});},
View
@@ -0,0 +1,61 @@
+(function() {
+ var matching = {"(": ")>", ")": "(<", "[": "]>", "]": "[<", "{": "}>", "}": "{<"};
+ function findMatchingBracket(cm) {
+ var cur = cm.getCursor(), line = cm.getLineHandle(cur.line), pos = cur.ch - 1;
+ var match = (pos >= 0 && matching[line.text.charAt(pos)]) || matching[line.text.charAt(++pos)];
+ if (!match) return null;
+ var ch = match.charAt(0), forward = match.charAt(1) == ">", d = forward ? 1 : -1;
+ var style = cm.getTokenAt({line: cur.line, ch: pos + 1}).className;
+
+ var stack = [line.text.charAt(pos)], re = /[(){}[\]]/;
+ function scan(line, lineNo, start) {
+ if (!line.text) return;
+ var pos = forward ? 0 : line.text.length - 1, end = forward ? line.text.length : -1;
+ if (start != null) pos = start + d;
+ for (; pos != end; pos += d) {
+ var ch = line.text.charAt(pos);
+ if (re.test(ch) && cm.getTokenAt({line: lineNo, ch: pos + 1}).className == style) {
+ var match = matching[ch];
+ if (match.charAt(1) == ">" == forward) stack.push(ch);
+ else if (stack.pop() != match.charAt(0)) return {pos: pos, match: false};
+ else if (!stack.length) return {pos: pos, match: true};
+ }
+ }
+ }
+ for (var i = cur.line, found, e = forward ? Math.min(i + 100, cm.lineCount()) : Math.max(-1, i - 100); i != e; i+=d) {
+ if (i == cur.line) found = scan(line, i, pos);
+ else found = scan(cm.getLineHandle(i), i);
+ if (found) break;
+ }
+ return {from: {line: cur.line, ch: pos}, to: found && {line: i, ch: found.pos}, match: found && found.match};
+ }
+
+ function matchBrackets(cm, autoclear) {
+ var found = findMatchingBracket(cm);
+ if (!found) return;
+ var style = found.match ? "CodeMirror-matchingbracket" : "CodeMirror-nonmatchingbracket";
+ var one = cm.markText(found.from, {line: found.from.line, ch: found.from.ch + 1}, style);
+ var two = found.to && cm.markText(found.to, {line: found.to.line, ch: found.to.ch + 1}, style);
+ var clear = function() {
+ cm.operation(function() { one.clear(); two && two.clear(); });
+ };
+ if (autoclear) setTimeout(clear, 800);
+ else return clear;
+ }
+
+ var currentlyHighlighted = null;
+ function doMatchBrackets(cm) {
+ setTimeout(cm.operation(function() {
+ if (currentlyHighlighted) {currentlyHighlighted(); currentlyHighlighted = null;}
+ if (!cm.somethingSelected()) currentlyHighlighted = matchBrackets(cm, false);
+ }), 20);
+ }
+
+ CodeMirror.defineOption("matchBrackets", false, function(cm, val) {
+ if (val) cm.connect("cursorActivity", doMatchBrackets);
+ else cm.disconnect("cursorActivity", doMatchBrackets);
+ });
+
+ CodeMirror.defineExtension("matchBrackets", function() {matchBrackets(this, true);});
+ CodeMirror.defineExtension("findMatchingBracket", function(){return findMatchingBracket(this);});
+})();
View
@@ -5,6 +5,7 @@
<title>CodeMirror: C-like mode</title>
<link rel="stylesheet" href="../../lib/codemirror.css">
<script src="../../lib/codemirror.js"></script>
+ <script src="../../lib/util/matchbrackets.js"></script>
<script src="clike.js"></script>
<link rel="stylesheet" href="../../doc/docs.css">
<style>.CodeMirror {border: 2px inset #dee;}</style>
View
@@ -6,6 +6,7 @@
<link rel="stylesheet" href="../../lib/codemirror.css">
<link rel="stylesheet" href="../../theme/ambiance.css">
<script src="../../lib/codemirror.js"></script>
+ <script src="../../lib/util/matchbrackets.js"></script>
<script src="clike.js"></script>
<link rel="stylesheet" href="../../doc/docs.css">
<style>
View
@@ -29,10 +29,7 @@ <h1>CodeMirror: ECL mode</h1>
output(d);
</textarea></form>
<script>
- var editor = CodeMirror.fromTextArea(document.getElementById("code"), {
- tabMode: "indent",
- matchBrackets: true,
- });
+ var editor = CodeMirror.fromTextArea(document.getElementById("code"), {});
</script>
<p>Based on CodeMirror's clike mode. For more information see <a href="http://hpccsystems.com">HPCC Systems</a> web site.</p>
View
@@ -5,6 +5,7 @@
<title>CodeMirror: Erlang mode</title>
<link rel="stylesheet" href="../../lib/codemirror.css">
<script src="../../lib/codemirror.js"></script>
+ <script src="../../lib/util/matchbrackets.js"></script>
<script src="erlang.js"></script>
<link rel="stylesheet" href="../../theme/erlang-dark.css">
<style type="text/css">.CodeMirror {border-top: 1px solid black; border-bottom: 1px solid black;}</style>
View
@@ -39,7 +39,6 @@ <h1>CodeMirror: GFM mode</h1>
var editor = CodeMirror.fromTextArea(document.getElementById("code"), {
mode: 'gfm',
lineNumbers: true,
- matchBrackets: true,
theme: "default"
});
</script>
View
@@ -6,6 +6,7 @@
<link rel="stylesheet" href="../../lib/codemirror.css">
<link rel="stylesheet" href="../../theme/elegant.css">
<script src="../../lib/codemirror.js"></script>
+ <script src="../../lib/util/matchbrackets.js"></script>
<script src="go.js"></script>
<link rel="stylesheet" href="../../doc/docs.css">
<style>.CodeMirror {border:1px solid #999; background:#ffc}</style>
View
@@ -5,6 +5,7 @@
<title>CodeMirror: Groovy mode</title>
<link rel="stylesheet" href="../../lib/codemirror.css">
<script src="../../lib/codemirror.js"></script>
+ <script src="../../lib/util/matchbrackets.js"></script>
<script src="groovy.js"></script>
<link rel="stylesheet" href="../../doc/docs.css">
<style>.CodeMirror {border-top: 1px solid #500; border-bottom: 1px solid #500;}</style>
View
@@ -5,6 +5,7 @@
<title>CodeMirror: Haskell mode</title>
<link rel="stylesheet" href="../../lib/codemirror.css">
<script src="../../lib/codemirror.js"></script>
+ <script src="../../lib/util/matchbrackets.js"></script>
<script src="haskell.js"></script>
<link rel="stylesheet" href="../../theme/elegant.css">
<style type="text/css">.CodeMirror {border-top: 1px solid black; border-bottom: 1px solid black;}</style>
View
@@ -80,7 +80,6 @@ <h1>CodeMirror: Haxe mode</h1>
<script>
var editor = CodeMirror.fromTextArea(document.getElementById("code"), {
lineNumbers: true,
- matchBrackets: true,
indentUnit: 4,
indentWithTabs: true
});
@@ -32,7 +32,6 @@ <h1>CodeMirror: Html Embedded Scripts mode</h1>
<script>
var editor = CodeMirror.fromTextArea(document.getElementById("code"), {
lineNumbers: true,
- matchBrackets: true,
mode: "application/x-ejs",
indentUnit: 4,
indentWithTabs: true,
@@ -5,6 +5,7 @@
<title>CodeMirror: JavaScript mode</title>
<link rel="stylesheet" href="../../lib/codemirror.css">
<script src="../../lib/codemirror.js"></script>
+ <script src="../../lib/util/matchbrackets.js"></script>
<script src="javascript.js"></script>
<link rel="stylesheet" href="../../doc/docs.css">
<style type="text/css">.CodeMirror {border-top: 1px solid black; border-bottom: 1px solid black;}</style>
View
@@ -5,6 +5,7 @@
<title>CodeMirror: LESS mode</title>
<link rel="stylesheet" href="../../lib/codemirror.css">
<script src="../../lib/codemirror.js"></script>
+ <script src="../../lib/util/matchbrackets.js"></script>
<script src="less.js"></script>
<style>.CodeMirror {background: #f8f8f8; border: 1px solid #ddd; font-size:12px} .CodeMirror-scroll {height: 400px}</style>
<link rel="stylesheet" href="../../doc/docs.css">
View
@@ -4,6 +4,7 @@
<meta charset="utf-8">
<title>CodeMirror: Lua mode</title>
<link rel="stylesheet" href="../../lib/codemirror.css">
+ <script src="../../lib/util/matchbrackets.js"></script>
<script src="../../lib/codemirror.js"></script>
<script src="lua.js"></script>
<link rel="stylesheet" href="../../theme/neat.css">
View
@@ -328,7 +328,6 @@ <h1>CodeMirror: Markdown mode</h1>
var editor = CodeMirror.fromTextArea(document.getElementById("code"), {
mode: 'markdown',
lineNumbers: true,
- matchBrackets: true,
theme: "default"
});
</script>
View
@@ -31,8 +31,7 @@ <h1>CodeMirror: MySQL mode</h1>
<script>
var editor = CodeMirror.fromTextArea(document.getElementById("code"), {
mode: "text/x-mysql",
- tabMode: "indent",
- matchBrackets: true
+ tabMode: "indent"
});
</script>
View
@@ -10,6 +10,7 @@
</style>
<script src=../../lib/codemirror.js></script>
+<script src=../../lib/util/matchbrackets.js></script>
<script src=ocaml.js></script>
<h1>CodeMirror: OCaml mode</h1>
View
@@ -39,7 +39,6 @@ <h1>CodeMirror: Pascal mode</h1>
<script>
var editor = CodeMirror.fromTextArea(document.getElementById("code"), {
lineNumbers: true,
- matchBrackets: true,
mode: "text/x-pascal"
});
</script>
Oops, something went wrong.

0 comments on commit 3721870

Please sign in to comment.