Add xml/html tag autoclosing behavior

This commit is contained in:
Daniil Kostion 2012-07-02 09:03:21 +10:00
commit 1104789217
4 changed files with 143 additions and 24 deletions

View file

@ -0,0 +1,98 @@
/* vim:ts=4:sts=4:sw=4:
* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is Ajax.org Code Editor (ACE).
*
* The Initial Developer of the Original Code is
* Ajax.org B.V.
* Portions created by the Initial Developer are Copyright (C) 2010
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Chris Spencer <chris.ag.spencer AT googlemail DOT com>
*
* Alternatively, the contents of this file may be used under the terms of
* either the GNU General Public License Version 2 or later (the "GPL"), or
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
* in which case the provisions of the GPL or the LGPL are applicable instead
* of those above. If you wish to allow use of your version of this file only
* under the terms of either the GPL or the LGPL, and not to allow others to
* use your version of this file under the terms of the MPL, indicate your
* decision by deleting the provisions above and replace them with the notice
* and other provisions required by the GPL or the LGPL. If you do not delete
* the provisions above, a recipient may use your version of this file under
* the terms of any one of the MPL, the GPL or the LGPL.
*
* ***** END LICENSE BLOCK ***** */
define(function(require, exports, module) {
"use strict";
var oop = require("../../lib/oop");
var XmlBehaviour = require("../behaviour/xml").XmlBehaviour;
var CstyleBehaviour = require("./cstyle").CstyleBehaviour;
var TokenIterator = require("../../token_iterator").TokenIterator;
var selfClosers = ['area', 'base', 'br', 'col', 'command', 'embed', 'hr', 'iframe', 'img', 'input', 'keygen', 'link', 'map', 'meta', 'param', 'source', 'track'];
function hasType(token, type) {
var hasType = true;
var typeList = token.type.split('.');
var needleList = type.split('.');
needleList.forEach(function(needle){
if (typeList.indexOf(needle) == -1) {
hasType = false;
return false;
}
});
return hasType;
}
var HtmlBehaviour = function () {
this.inherit(XmlBehaviour); // Get xml behaviour
this.add("autoclosing", "insertion", function (state, action, editor, session, text) {
if (text == '>') {
var position = editor.getCursorPosition();
var iterator = new TokenIterator(session, position.row, position.column);
var token = iterator.getCurrentToken();
var atCursor = false;
if (!token || !hasType(token, 'meta.tag') && !(hasType(token, 'text') && token.value.match('/'))){
do {
token = iterator.stepBackward();
} while (token && (hasType(token, 'string') || hasType(token, 'keyword.operator') || hasType(token, 'entity.attribute-name') || hasType(token, 'text')));
} else {
atCursor = true;
}
if (!token || !hasType(token, 'meta.tag-name') || iterator.stepBackward().value.match('/')) {
return
}
var tag = token.value;
if (atCursor){
var tag = tag.substring(0, position.column - token.start);
}
if (selfClosers.indexOf(tag) !== -1){
return;
}
return {
text: '>' + '</' + tag + '>',
selection: [1, 1]
}
}
});
}
oop.inherits(HtmlBehaviour, XmlBehaviour);
exports.HtmlBehaviour = HtmlBehaviour;
});

View file

@ -42,34 +42,55 @@ define(function(require, exports, module) {
var oop = require("../../lib/oop");
var Behaviour = require("../behaviour").Behaviour;
var CstyleBehaviour = require("./cstyle").CstyleBehaviour;
var TokenIterator = require("../../token_iterator").TokenIterator;
function hasType(token, type) {
var hasType = true;
var typeList = token.type.split('.');
var needleList = type.split('.');
needleList.forEach(function(needle){
if (typeList.indexOf(needle) == -1) {
hasType = false;
return false;
}
});
return hasType;
}
var XmlBehaviour = function () {
this.inherit(CstyleBehaviour, ["string_dquotes"]); // Get string behaviour
this.add("brackets", "insertion", function (state, action, editor, session, text) {
if (text == '<') {
var selection = editor.getSelectionRange();
var selected = session.doc.getTextRange(selection);
if (selected !== "") {
return false;
this.add("autoclosing", "insertion", function (state, action, editor, session, text) {
if (text == '>') {
var position = editor.getCursorPosition();
var iterator = new TokenIterator(session, position.row, position.column);
var token = iterator.getCurrentToken();
var atCursor = false;
if (!token || !hasType(token, 'meta.tag') && !(hasType(token, 'text') && token.value.match('/'))){
do {
token = iterator.stepBackward();
} while (token && (hasType(token, 'string') || hasType(token, 'keyword.operator') || hasType(token, 'entity.attribute-name') || hasType(token, 'text')));
} else {
return {
text: '<>',
selection: [1, 1]
}
atCursor = true;
}
} else if (text == '>') {
var cursor = editor.getCursorPosition();
var line = session.doc.getLine(cursor.row);
var rightChar = line.substring(cursor.column, cursor.column + 1);
if (rightChar == '>') { // need some kind of matching check here
return {
text: '',
selection: [1, 1]
}
if (!token || !hasType(token, 'meta.tag-name') || iterator.stepBackward().value.match('/')) {
return
}
} else if (text == "\n") {
var tag = token.value;
if (atCursor){
var tag = tag.substring(0, position.column - token.start);
}
return {
text: '>' + '</' + tag + '>',
selection: [1, 1]
}
}
});
this.add('autoindent', 'insertion', function (state, action, editor, session, text) {
if (text == "\n") {
var cursor = editor.getCursorPosition();
var line = session.doc.getLine(cursor.row);
var rightChars = line.substring(cursor.column, cursor.column + 2);

View file

@ -44,13 +44,13 @@ var JavaScriptMode = require("./javascript").Mode;
var CssMode = require("./css").Mode;
var Tokenizer = require("../tokenizer").Tokenizer;
var HtmlHighlightRules = require("./html_highlight_rules").HtmlHighlightRules;
var XmlBehaviour = require("./behaviour/xml").XmlBehaviour;
var HtmlBehaviour = require("./behaviour/html").HtmlBehaviour;
var HtmlFoldMode = require("./folding/html").FoldMode;
var Mode = function() {
var highlighter = new HtmlHighlightRules();
this.$tokenizer = new Tokenizer(highlighter.getRules());
this.$behaviour = new XmlBehaviour();
this.$behaviour = new HtmlBehaviour();
this.$embeds = highlighter.getEmbeds();
this.createModeDelegates({

View file

@ -80,9 +80,9 @@ exports.tag = function(states, name, nextState, tagMap) {
token : function(value) {
if (tagMap && tagMap[value]) {
return "meta.tag" + '.' + tagMap[value];
return "meta.tag.tag-name" + '.' + tagMap[value];
} else {
return "meta.tag";
return "meta.tag.tag-name";
}
},
merge : true,