diff --git a/lib/ace/edit_session/bracket_match.js b/lib/ace/edit_session/bracket_match.js index 878cff13..d936296f 100644 --- a/lib/ace/edit_session/bracket_match.js +++ b/lib/ace/edit_session/bracket_match.js @@ -77,43 +77,46 @@ function BracketMatch() { var token = iterator.getCurrentToken(); if (!token) return null; - // Create a pattern that matches any token with the same type as token.type. - // Exception: if token.type includes "rparen", then also match "lparen". + // token.type contains a period-delimited list of token identifiers + // (e.g.: "constant.numeric" or "paren.lparen"). Create a pattern that + // matches any token containing the same identifiers or a subset. In + // addition, if token.type includes "rparen", then also match "lparen". + // So if type.token is "paren.rparen", then typeRe will match "lparen.paren". var typeRe = new RegExp("(\\.?[" + token.type.replace(".", "|").replace("rparen", "lparen|rparen") + "])+"); // Start searching in token, just before the character at position.column - var vIndex = position.column - iterator.getCurrentTokenColumn() - 2; + var valueIndex = position.column - iterator.getCurrentTokenColumn() - 2; var value = token.value; while (true) { - while (vIndex >= 0) { - var char = value.charAt(vIndex); + while (valueIndex >= 0) { + var char = value.charAt(valueIndex); if (char == openBracket) { depth -= 1; if (depth == 0) { return {row: iterator.getCurrentTokenRow(), - column: vIndex + iterator.getCurrentTokenColumn()}; + column: valueIndex + iterator.getCurrentTokenColumn()}; } } else if (char == bracket) { depth += 1; } - vIndex -= 1; + valueIndex -= 1; } // Scan backward through the document, looking for the next token // whose type matches typeRe do { token = iterator.stepBackward(); - } while (token && !typeRe.test(token.type)) + } while (token && !typeRe.test(token.type)); if (token == null) break; value = token.value; - vIndex = token.value.length - 1; + valueIndex = value.length - 1; } return null; @@ -127,43 +130,46 @@ function BracketMatch() { var token = iterator.getCurrentToken(); if (!token) return null; - // Create a pattern that matches any token with the same type as token.type. - // Exception: if token.type includes "lparen", then also match "rparen". + // token.type contains a period-delimited list of token identifiers + // (e.g.: "constant.numeric" or "paren.lparen"). Create a pattern that + // matches any token containing the same identifiers or a subset. In + // addition, if token.type includes "lparen", then also match "rparen". + // So if type.token is "lparen.paren", then typeRe will match "paren.rparen". var typeRe = new RegExp("(\\.?[" + token.type.replace(".", "|").replace("lparen", "lparen|rparen") + "])+"); - // Start searching in token, after after the character at position.column - var vIndex = position.column - iterator.getCurrentTokenColumn(); + // Start searching in token, after the character at position.column + var valueIndex = position.column - iterator.getCurrentTokenColumn(); while (true) { var value = token.value; var valueLength = value.length; - while (vIndex < valueLength) { - var char = value.charAt(vIndex); + while (valueIndex < valueLength) { + var char = value.charAt(valueIndex); if (char == closingBracket) { depth -= 1; if (depth == 0) { return {row: iterator.getCurrentTokenRow(), - column: vIndex + iterator.getCurrentTokenColumn()}; + column: valueIndex + iterator.getCurrentTokenColumn()}; } } else if (char == bracket) { depth += 1; } - vIndex += 1; + valueIndex += 1; } // Scan forward through the document, looking for the next token // whose type matches typeRe do { token = iterator.stepForward(); - } while (token && !typeRe.test(token.type)) + } while (token && !typeRe.test(token.type)); if (token == null) break; - vIndex = 0; + valueIndex = 0; } return null; diff --git a/lib/ace/edit_session_test.js b/lib/ace/edit_session_test.js index effd8c62..49957932 100644 --- a/lib/ace/edit_session_test.js +++ b/lib/ace/edit_session_test.js @@ -91,7 +91,7 @@ module.exports = { assert.equal(session.findMatchingBracket({row: 0, column: 0}), null); }, - "test: find matching opening bracket in JavaScript mode" : function() { + "test: find matching opening bracket in JavaScript mode" : function() { var lines = [ "function foo() {", " var str = \"{ foo()\";", @@ -112,8 +112,8 @@ module.exports = { assert.position(session.findMatchingBracket({row: 4, column: 24}), 4, 19); assert.equal(session.findMatchingBracket({row: 0, column: 1}), null); }, - - "test: find matching closing bracket in JavaScript mode" : function() { + + "test: find matching closing bracket in JavaScript mode" : function() { var lines = [ "function foo() {", " var str = \"{ foo()\";", @@ -134,6 +134,24 @@ module.exports = { assert.position(session.findMatchingBracket({row: 4, column: 20}), 4, 23); }, + "test: handle unbalanced brackets in JavaScript mode" : function() { + var lines = [ + "function foo() {", + " var str = \"{ foo()\";", + " if (debug) {", + " // write str a string) to the console", + " console.log(str);", + " ", + " str += \" bar() \";", + "}" + ]; + var session = new EditSession(lines.join("\n"), new JavaScriptMode()); + + assert.equal(session.findMatchingBracket({row: 0, column: 16}), null); + assert.equal(session.findMatchingBracket({row: 3, column: 30}), null); + assert.equal(session.findMatchingBracket({row: 1, column: 16}), null); + }, + "test: match different bracket types" : function() { var session = new EditSession(["({[", ")]}"]);