Permalink
Browse files

Upgraded HTML matcher, need battle tests

  • Loading branch information...
sergeche committed Dec 3, 2012
1 parent b0390cf commit 29a5c4362647905958efb73199f7acfeec687e56
View
@@ -231,40 +231,15 @@ emmet.define('actionUtils', function(require, _) {
* @returns {String}
*/
detectSyntax: function(editor, hint) {
- var caretPos = editor.getCaretPos();
- var content = String(editor.getContent());
var syntax = hint || 'html';
- if (!require('resources').hasSyntax(syntax))
+ if (!require('resources').hasSyntax(syntax)) {
syntax = 'html';
-
- if (syntax == 'html') {
- // are we inside <style> tag?
- var tag = require('htmlMatcher').tag(content, caretPos);
- if (tag && tag.open.name.toLowerCase() == 'style' && tag.innerRange.cmp(caretPos, 'lte', 'gte')) {
- syntax = 'css';
- }
-
-// var pair = require('html_matcher').getTags(editor.getContent(), caretPos);
-// if (pair && pair[0] && pair[0].type == 'tag' && pair[0].name.toLowerCase() == 'style') {
-// // check that we're actually inside the tag
-// if (pair[0].end <= caretPos && pair[1].start >= caretPos)
-// syntax = 'css';
-// }
}
- if (syntax == 'html') {
- // are we inside style attribute?
- var tree = require('xmlEditTree').parseFromPosition(editor.getContent(), caretPos, true);
- if (tree) {
- var attr = tree.itemFromPosition(caretPos, true);
- if (attr && attr.name().toLowerCase() == 'style') {
- var range = attr.valueRange(true);
- if (range.start <= caretPos && range.end >= caretPos)
- syntax = 'css';
- }
- }
- }
+ if (syntax == 'html' && (this.isStyle(editor) || this.isInlineCSS(editor))) {
+ syntax = 'css';
+ }
return syntax;
},
@@ -280,6 +255,10 @@ emmet.define('actionUtils', function(require, _) {
case 'xsl':
return 'xml';
case 'html':
+ if (this.isInlineCSS(editor)) {
+ return 'line';
+ }
+
var profile = require('resources').getVariable('profile');
if (!profile) { // no forced profile, guess from content
// html or xhtml?
@@ -299,6 +278,38 @@ emmet.define('actionUtils', function(require, _) {
*/
isXHTML: function(editor) {
return editor.getContent().search(/<!DOCTYPE[^>]+XHTML/i) != -1;
+ },
+
+ /**
+ * Check if current caret position is inside &lt;style&gt; tag
+ * @param {IEmmetEditor} editor
+ * @returns
+ */
+ isStyle: function(editor) {
+ var content = String(editor.getContent());
+ var caretPos = editor.getCaretPos();
+ var tag = require('htmlMatcher').tag(content, caretPos);
+ return tag && tag.open.name.toLowerCase() == 'style'
+ && tag.innerRange.cmp(caretPos, 'lte', 'gte');
+ },
+
+ /**
+ * Check if current caret position is inside "style" attribute of HTML
+ * element
+ * @param {IEmmetEditor} editor
+ * @returns {Boolean}
+ */
+ isInlineCSS: function(editor) {
+ var content = String(editor.getContent());
+ var caretPos = editor.getCaretPos();
+ var tree = require('xmlEditTree').parseFromPosition(content, caretPos, true);
+ if (tree) {
+ var attr = tree.itemFromPosition(caretPos, true);
+ return attr && attr.name().toLowerCase() == 'style'
+ && attr.valueRange(true).cmp(caretPos, 'lte', 'gte');
+ }
+
+ return false;
}
};
});
@@ -37,18 +37,10 @@ emmet.exec(function(require, _) {
var pad = res.getVariable('indentation');
// let's see if we're breaking newly created tag
var tag = require('htmlMatcher').tag(info.content, caretPos);
- if (tag && !tag.innerRange.length) {
+ if (tag && !tag.innerRange.length()) {
editor.replaceContent(nl + pad + utils.getCaretPlaceholder() + nl, caretPos);
return true;
}
-
-
-// var pair = require('html_matcher').getTags(info.content, caretPos, info.profile);
-//
-// if (pair[0] && pair[1] && pair[0].type == 'tag' && pair[0].end == caretPos && pair[1].start == caretPos) {
-// editor.replaceContent(nl + pad + utils.getCaretPlaceholder() + nl, caretPos);
-// return true;
-// }
} else if (info.syntax == 'css') {
/** @type String */
var content = info.content;
@@ -8,62 +8,68 @@
emmet.exec(function(require, _) {
/** @type emmet.actions */
var actions = require('actions');
- var matcher = require('html_matcher');
+ var matcher = require('htmlMatcher');
+ var lastMatch = null;
/**
* Find and select HTML tag pair
* @param {IEmmetEditor} editor Editor instance
* @param {String} direction Direction of pair matching: 'in' or 'out'.
* Default is 'out'
*/
- function matchPair(editor, direction, syntax) {
+ function matchPair(editor, direction) {
direction = String((direction || 'out').toLowerCase());
- var info = require('editorUtils').outputInfo(editor, syntax);
- syntax = info.syntax;
+ var info = require('editorUtils').outputInfo(editor);
var range = require('range');
/** @type Range */
- var selRange = range.create(editor.getSelectionRange());
+ var sel = range.create(editor.getSelectionRange());
var content = info.content;
- /** @type Range */
- var tagRange = null;
- /** @type Range */
- var _r;
- var oldOpenTag = matcher.last_match['opening_tag'];
- var oldCloseTag = matcher.last_match['closing_tag'];
-
- if (direction == 'in' && oldOpenTag && selRange.length()) {
-// user has previously selected tag and wants to move inward
- if (!oldCloseTag) {
-// unary tag was selected, can't move inward
- return false;
- } else if (oldOpenTag.start == selRange.start) {
- if (content.charAt(oldOpenTag.end) == '<') {
-// test if the first inward tag matches the entire parent tag's content
- _r = range.create(matcher.find(content, oldOpenTag.end + 1, syntax));
- if (_r.start == oldOpenTag.end && _r.end == oldCloseTag.start) {
- tagRange = range.create(matcher(content, oldOpenTag.end + 1, syntax));
+ // validate previous match
+ if (lastMatch && !lastMatch.range.equal(sel)) {
+ lastMatch = null;
+ }
+
+ if (lastMatch && sel.length()) {
+ if (direction == 'in') {
+ // user has previously selected tag and wants to move inward
+ if (lastMatch.type == 'tag' && !lastMatch.close) {
+ // unary tag was selected, can't move inward
+ return false;
+ } else {
+ if (lastMatch.range.equal(lastMatch.outerRange)) {
+ lastMatch.range = lastMatch.innerRange;
} else {
- tagRange = range.create(oldOpenTag.end, oldCloseTag.start - oldOpenTag.end);
+ lastMatch = matcher.find(content, lastMatch.open.range.end + 1);
+ if (lastMatch.range.equal(sel) && lastMatch.outerRange.equal(sel)) {
+ lastMatch.range = lastMatch.innerRange;
+ }
}
- } else {
- tagRange = range.create(oldOpenTag.end, oldCloseTag.start - oldOpenTag.end);
}
} else {
- var newCursor = content.substring(0, oldCloseTag.start).indexOf('<', oldOpenTag.end);
- var searchPos = newCursor != -1 ? newCursor + 1 : oldOpenTag.end;
- tagRange = range.create(matcher(content, searchPos, syntax));
+ if (
+ !lastMatch.innerRange.equal(lastMatch.outerRange)
+ && lastMatch.range.equal(lastMatch.innerRange)
+ && sel.equal(lastMatch.range)) {
+ lastMatch.range = lastMatch.outerRange;
+ } else {
+ lastMatch = matcher.find(content, sel.start);
+ if (lastMatch.range.equal(sel) && lastMatch.innerRange.equal(sel)) {
+ lastMatch.range = lastMatch.outerRange;
+ }
+ }
}
} else {
- tagRange = range.create(matcher(content, selRange.end, syntax));
+ lastMatch = matcher.find(content, sel.start);
}
- if (tagRange && tagRange.start != -1) {
- editor.createSelection(tagRange.start, tagRange.end);
+ if (lastMatch && !lastMatch.range.equal(sel)) {
+ editor.createSelection(lastMatch.range.start, lastMatch.range.end);
return true;
}
+ lastMatch = null;
return false;
}
@@ -88,22 +94,15 @@ emmet.exec(function(require, _) {
// looks like caret is outside of tag pair
caretPos++;
- var tags = matcher.getTags(content, caretPos, String(editor.getProfileName()));
-
- if (tags && tags[0]) {
- // match found
- var openTag = tags[0];
- var closeTag = tags[1];
-
- if (closeTag) { // exclude unary tags
- if (openTag.start <= caretPos && openTag.end >= caretPos) {
- editor.setCaretPos(closeTag.start);
- return true;
- } else if (closeTag.start <= caretPos && closeTag.end >= caretPos){
- editor.setCaretPos(openTag.start);
- return true;
- }
+ var tag = matcher.tag(content, caretPos);
+ if (tag && tag.close) { // exclude unary tags
+ if (tag.open.range.inside(caretPos)) {
+ editor.setCaretPos(tag.close.range.start);
+ } else {
+ editor.setCaretPos(tag.open.range.start);
}
+
+ return true;
}
return false;
@@ -5,7 +5,7 @@
*/
emmet.exec(function(require, _) {
require('actions').add('merge_lines', function(editor) {
- var matcher = require('html_matcher');
+ var matcher = require('htmlMatcher');
var utils = require('utils');
var editorUtils = require('editorUtils');
var info = editorUtils.outputInfo(editor);
@@ -14,10 +14,9 @@ emmet.exec(function(require, _) {
var selection = require('range').create(editor.getSelectionRange());
if (!selection.length()) {
// find matching tag
- var pair = matcher(info.content, editor.getCaretPos(), info.profile);
+ var pair = matcher.find(info.content, editor.getCaretPos());
if (pair) {
- selection.start = pair[0];
- selection.end = pair[1];
+ selection = pair.outerRange;
}
}
@@ -10,22 +10,22 @@ emmet.exec(function(require, _) {
var info = require('editorUtils').outputInfo(editor);
// search for tag
- var pair = require('html_matcher').getTags(info.content, editor.getCaretPos(), info.profile);
- if (pair && pair[0]) {
- if (!pair[1]) {
+ var tag = require('htmlMatcher').tag(info.content, editor.getCaretPos());
+ if (tag) {
+ if (!tag.close) {
// simply remove unary tag
- editor.replaceContent(utils.getCaretPlaceholder(), pair[0].start, pair[0].end);
+ editor.replaceContent(utils.getCaretPlaceholder(), tag.range.start, tag.range.end);
} else {
// remove tag and its newlines
/** @type Range */
- var tagContentRange = utils.narrowToNonSpace(info.content, pair[0].end, pair[1].start - pair[0].end);
+ var tagContentRange = utils.narrowToNonSpace(info.content, tag.innerRange);
/** @type Range */
var startLineBounds = utils.findNewlineBounds(info.content, tagContentRange.start);
var startLinePad = utils.getLinePadding(startLineBounds.substring(info.content));
var tagContent = tagContentRange.substring(info.content);
tagContent = utils.unindentString(tagContent, startLinePad);
- editor.replaceContent(utils.getCaretPlaceholder() + utils.escapeText(tagContent), pair[0].start, pair[1].end);
+ editor.replaceContent(utils.getCaretPlaceholder() + utils.escapeText(tagContent), tag.outerRange.start, tag.outerRange.end);
}
return true;
@@ -11,28 +11,27 @@ emmet.exec(function(require, _) {
/**
* @param {IEmmetEditor} editor
* @param {Object} profile
- * @param {Object} htmlMatch
+ * @param {Object} tag
*/
- function joinTag(editor, profile, htmlMatch) {
+ function joinTag(editor, profile, tag) {
/** @type emmet.utils */
var utils = require('utils');
- var closingSlash = (profile.self_closing_tag === true) ? '/' : ' /';
- var content = htmlMatch[0].full_tag.replace(/\s*>$/, closingSlash + '>');
+ var content = tag.open.range.substring(tag.source).replace(/\s*>$/, profile.selfClosing() + '>');
// add caret placeholder
- if (content.length + htmlMatch[0].start < editor.getCaretPos())
+ if (content.length + tag.outerRange.start < editor.getCaretPos())
content += utils.getCaretPlaceholder();
else {
- var d = editor.getCaretPos() - htmlMatch[0].start;
+ var d = editor.getCaretPos() - tag.outerRange.start;
content = utils.replaceSubstring(content, utils.getCaretPlaceholder(), d);
}
- editor.replaceContent(content, htmlMatch[0].start, htmlMatch[1].end);
+ editor.replaceContent(content, tag.outerRange.start, tag.outerRange.end);
return true;
}
- function splitTag(editor, profile, htmlMatch) {
+ function splitTag(editor, profile, tag) {
/** @type emmet.utils */
var utils = require('utils');
@@ -42,25 +41,24 @@ emmet.exec(function(require, _) {
// define tag content depending on profile
var tagContent = (profile.tag_nl === true) ? nl + pad + caret + nl : caret;
+ var content = tag.outerContent().replace(/\s*\/>$/, '>') + tagContent + '</' + tag.open.name + '>';
- var content = htmlMatch[0].full_tag.replace(/\s*\/>$/, '>') + tagContent + '</' + htmlMatch[0].name + '>';
- editor.replaceContent(content, htmlMatch[0].start, htmlMatch[0].end);
+ editor.replaceContent(content, tag.outerRange.start, tag.outerRange.end);
return true;
}
require('actions').add('split_join_tag', function(editor, profileName) {
- var matcher = require('html_matcher');
+ var matcher = require('htmlMatcher');
var info = require('editorUtils').outputInfo(editor, null, profileName);
var profile = require('profile').get(info.profile);
// find tag at current position
- var pair = matcher.getTags(info.content, editor.getCaretPos(), info.profile);
- if (pair && pair[0]) {
- if (pair[1]) { // join tag
- return joinTag(editor, profile, pair);
- }
- return splitTag(editor, profile, pair);
+ var tag = matcher.tag(info.content, editor.getCaretPos());
+ if (tag) {
+ return tag.close
+ ? joinTag(editor, profile, tag)
+ : splitTag(editor, profile, tag);
}
return false;
@@ -21,10 +21,9 @@ emmet.exec(function(require, _) {
if (!range.length()) {
// no selection, find matching tag
- var pair = require('html_matcher').getTags(info.content, editor.getCaretPos(), info.profile);
- if (pair && pair[0]) { // found pair
- range.start = pair[0].start;
- range.end = pair[1] ? pair[1].end : pair[0].end;
+ var tag = require('htmlMatcher').tag(info.content, editor.getCaretPos());
+ if (tag) { // found pair
+ range = tag.outerRange;
}
}
@@ -197,9 +196,8 @@ emmet.exec(function(require, _) {
// current token, we have to make sure that cursor is not inside
// 'style' attribute of html element
var caretPos = editor.getCaretPos();
- var pair = require('html_matcher').getTags(info.content, caretPos);
- if (pair && pair[0] && pair[0].type == 'tag' &&
- pair[0].start <= caretPos && pair[0].end >= caretPos) {
+ var tag = require('htmlMatcher').tag(info.content, caretPos);
+ if (tag && tag.open.range.inside(caretPos)) {
info.syntax = 'html';
}
}
Oops, something went wrong.

0 comments on commit 29a5c43

Please sign in to comment.