Skip to content

Commit

Permalink
Move matchBrackets out of the core
Browse files Browse the repository at this point in the history
Add CodeMirror.defineOption function for defining new options.

Add findMatchingBracket to the methods exported by this extension.

Closes codemirror#746
  • Loading branch information
marijnh committed Aug 28, 2012
1 parent 753e838 commit 3721870
Show file tree
Hide file tree
Showing 40 changed files with 113 additions and 85 deletions.
1 change: 0 additions & 1 deletion demo/changemode.html
Original file line number Diff line number Diff line change
Expand Up @@ -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() {
Expand Down
1 change: 1 addition & 0 deletions doc/compress.html
Original file line number Diff line number Diff line change
Expand Up @@ -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>
Expand Down
22 changes: 15 additions & 7 deletions doc/manual.html
Original file line number Diff line number Diff line change
Expand Up @@ -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>
Expand Down Expand Up @@ -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>

Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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.
Expand Down
61 changes: 12 additions & 49 deletions lib/codemirror.js
Original file line number Diff line number Diff line change
Expand Up @@ -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),
Expand All @@ -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);
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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)

Expand All @@ -2225,7 +2184,6 @@ window.CodeMirror = (function() {
firstLineNumber: 1,
readOnly: false,
dragDrop: true,
matchBrackets: false,
cursorBlinkRate: 530,
workTime: 100,
workDelay: 200,
Expand Down Expand Up @@ -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});},
Expand Down
61 changes: 61 additions & 0 deletions lib/util/matchbrackets.js
Original file line number Diff line number Diff line change
@@ -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);});
})();
1 change: 1 addition & 0 deletions mode/clike/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -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>
Expand Down
1 change: 1 addition & 0 deletions mode/clike/scala.html
Original file line number Diff line number Diff line change
Expand Up @@ -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>
Expand Down
5 changes: 1 addition & 4 deletions mode/ecl/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -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>
Expand Down
1 change: 1 addition & 0 deletions mode/erlang/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -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>
Expand Down
1 change: 0 additions & 1 deletion mode/gfm/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -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>
Expand Down
1 change: 1 addition & 0 deletions mode/go/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -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>
Expand Down
1 change: 1 addition & 0 deletions mode/groovy/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -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>
Expand Down
1 change: 1 addition & 0 deletions mode/haskell/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -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>
Expand Down
1 change: 0 additions & 1 deletion mode/haxe/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -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
});
Expand Down
1 change: 0 additions & 1 deletion mode/htmlembedded/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
1 change: 1 addition & 0 deletions mode/javascript/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -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>
Expand Down
1 change: 1 addition & 0 deletions mode/less/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -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">
Expand Down
1 change: 1 addition & 0 deletions mode/lua/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -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">
Expand Down
1 change: 0 additions & 1 deletion mode/markdown/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -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>
Expand Down
3 changes: 1 addition & 2 deletions mode/mysql/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -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>

Expand Down
1 change: 1 addition & 0 deletions mode/ocaml/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -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>
Expand Down
1 change: 0 additions & 1 deletion mode/pascal/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -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>
Expand Down
Loading

0 comments on commit 3721870

Please sign in to comment.