Merge pull request #827 from danyaPostfactum/test
Add xml/html tag autoclosing behavior
This commit is contained in:
commit
3a19dc503b
5 changed files with 168 additions and 56 deletions
98
lib/ace/mode/behaviour/html.js
Normal file
98
lib/ace/mode/behaviour/html.js
Normal 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 voidElements = ['area', 'base', 'br', 'col', 'command', 'embed', 'hr', 'img', 'input', 'keygen', 'link', 'meta', 'param', 'source', 'track', 'wbr'];
|
||||||
|
|
||||||
|
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 element = token.value;
|
||||||
|
if (atCursor){
|
||||||
|
var element = element.substring(0, position.column - token.start);
|
||||||
|
}
|
||||||
|
if (voidElements.indexOf(element) !== -1){
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
text: '>' + '</' + element + '>',
|
||||||
|
selection: [1, 1]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
oop.inherits(HtmlBehaviour, XmlBehaviour);
|
||||||
|
|
||||||
|
exports.HtmlBehaviour = HtmlBehaviour;
|
||||||
|
});
|
||||||
|
|
@ -42,34 +42,55 @@ define(function(require, exports, module) {
|
||||||
var oop = require("../../lib/oop");
|
var oop = require("../../lib/oop");
|
||||||
var Behaviour = require("../behaviour").Behaviour;
|
var Behaviour = require("../behaviour").Behaviour;
|
||||||
var CstyleBehaviour = require("./cstyle").CstyleBehaviour;
|
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 () {
|
var XmlBehaviour = function () {
|
||||||
|
|
||||||
this.inherit(CstyleBehaviour, ["string_dquotes"]); // Get string behaviour
|
this.inherit(CstyleBehaviour, ["string_dquotes"]); // Get string behaviour
|
||||||
|
|
||||||
this.add("brackets", "insertion", function (state, action, editor, session, text) {
|
this.add("autoclosing", "insertion", function (state, action, editor, session, text) {
|
||||||
if (text == '<') {
|
if (text == '>') {
|
||||||
var selection = editor.getSelectionRange();
|
var position = editor.getCursorPosition();
|
||||||
var selected = session.doc.getTextRange(selection);
|
var iterator = new TokenIterator(session, position.row, position.column);
|
||||||
if (selected !== "") {
|
var token = iterator.getCurrentToken();
|
||||||
return false;
|
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 {
|
} else {
|
||||||
return {
|
atCursor = true;
|
||||||
text: '<>',
|
|
||||||
selection: [1, 1]
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
} else if (text == '>') {
|
if (!token || !hasType(token, 'meta.tag-name') || iterator.stepBackward().value.match('/')) {
|
||||||
var cursor = editor.getCursorPosition();
|
return
|
||||||
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]
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
} 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 cursor = editor.getCursorPosition();
|
||||||
var line = session.doc.getLine(cursor.row);
|
var line = session.doc.getLine(cursor.row);
|
||||||
var rightChars = line.substring(cursor.column, cursor.column + 2);
|
var rightChars = line.substring(cursor.column, cursor.column + 2);
|
||||||
|
|
|
||||||
|
|
@ -44,13 +44,13 @@ var JavaScriptMode = require("./javascript").Mode;
|
||||||
var CssMode = require("./css").Mode;
|
var CssMode = require("./css").Mode;
|
||||||
var Tokenizer = require("../tokenizer").Tokenizer;
|
var Tokenizer = require("../tokenizer").Tokenizer;
|
||||||
var HtmlHighlightRules = require("./html_highlight_rules").HtmlHighlightRules;
|
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 HtmlFoldMode = require("./folding/html").FoldMode;
|
||||||
|
|
||||||
var Mode = function() {
|
var Mode = function() {
|
||||||
var highlighter = new HtmlHighlightRules();
|
var highlighter = new HtmlHighlightRules();
|
||||||
this.$tokenizer = new Tokenizer(highlighter.getRules());
|
this.$tokenizer = new Tokenizer(highlighter.getRules());
|
||||||
this.$behaviour = new XmlBehaviour();
|
this.$behaviour = new HtmlBehaviour();
|
||||||
|
|
||||||
this.$embeds = highlighter.getEmbeds();
|
this.$embeds = highlighter.getEmbeds();
|
||||||
this.createModeDelegates({
|
this.createModeDelegates({
|
||||||
|
|
|
||||||
|
|
@ -44,6 +44,25 @@ var JavaScriptHighlightRules = require("./javascript_highlight_rules").JavaScrip
|
||||||
var xmlUtil = require("./xml_util");
|
var xmlUtil = require("./xml_util");
|
||||||
var TextHighlightRules = require("./text_highlight_rules").TextHighlightRules;
|
var TextHighlightRules = require("./text_highlight_rules").TextHighlightRules;
|
||||||
|
|
||||||
|
var tagMap = {
|
||||||
|
a : 'anchor',
|
||||||
|
button : 'form',
|
||||||
|
form : 'form',
|
||||||
|
img : 'image',
|
||||||
|
input : 'form',
|
||||||
|
label : 'form',
|
||||||
|
script : 'script',
|
||||||
|
select : 'form',
|
||||||
|
textarea : 'form',
|
||||||
|
style : 'style',
|
||||||
|
table : 'table',
|
||||||
|
tbody : 'table',
|
||||||
|
td : 'table',
|
||||||
|
tfoot : 'table',
|
||||||
|
th : 'table',
|
||||||
|
tr : 'table'
|
||||||
|
};
|
||||||
|
|
||||||
var HtmlHighlightRules = function() {
|
var HtmlHighlightRules = function() {
|
||||||
|
|
||||||
// regexp must not have capturing parentheses
|
// regexp must not have capturing parentheses
|
||||||
|
|
@ -113,9 +132,9 @@ var HtmlHighlightRules = function() {
|
||||||
} ]
|
} ]
|
||||||
};
|
};
|
||||||
|
|
||||||
xmlUtil.tag(this.$rules, "tag", "start");
|
xmlUtil.tag(this.$rules, "tag", "start", tagMap);
|
||||||
xmlUtil.tag(this.$rules, "style", "css-start");
|
xmlUtil.tag(this.$rules, "style", "css-start", tagMap);
|
||||||
xmlUtil.tag(this.$rules, "script", "js-start");
|
xmlUtil.tag(this.$rules, "script", "js-start", tagMap);
|
||||||
|
|
||||||
this.embedRules(JavaScriptHighlightRules, "js-", [{
|
this.embedRules(JavaScriptHighlightRules, "js-", [{
|
||||||
token: "comment",
|
token: "comment",
|
||||||
|
|
|
||||||
|
|
@ -38,16 +38,6 @@
|
||||||
define(function(require, exports, module) {
|
define(function(require, exports, module) {
|
||||||
"use strict";
|
"use strict";
|
||||||
|
|
||||||
var lang = require("../lib/lang");
|
|
||||||
|
|
||||||
var formTags = lang.arrayToMap(
|
|
||||||
("button|form|input|label|select|textarea").split("|")
|
|
||||||
);
|
|
||||||
|
|
||||||
var tableTags = lang.arrayToMap(
|
|
||||||
("table|tbody|td|tfoot|th|tr").split("|")
|
|
||||||
);
|
|
||||||
|
|
||||||
function string(state) {
|
function string(state) {
|
||||||
return [{
|
return [{
|
||||||
token : "string",
|
token : "string",
|
||||||
|
|
@ -81,7 +71,7 @@ function multiLineString(quote, state) {
|
||||||
}];
|
}];
|
||||||
}
|
}
|
||||||
|
|
||||||
exports.tag = function(states, name, nextState) {
|
exports.tag = function(states, name, nextState, tagMap) {
|
||||||
states[name] = [{
|
states[name] = [{
|
||||||
token : "text",
|
token : "text",
|
||||||
regex : "\\s+"
|
regex : "\\s+"
|
||||||
|
|
@ -89,26 +79,10 @@ exports.tag = function(states, name, nextState) {
|
||||||
//token : "meta.tag",
|
//token : "meta.tag",
|
||||||
|
|
||||||
token : function(value) {
|
token : function(value) {
|
||||||
if ( value==='a' ) {
|
if (tagMap && tagMap[value]) {
|
||||||
return "meta.tag.anchor";
|
return "meta.tag.tag-name" + '.' + tagMap[value];
|
||||||
}
|
} else {
|
||||||
else if ( value==='img' ) {
|
return "meta.tag.tag-name";
|
||||||
return "meta.tag.image";
|
|
||||||
}
|
|
||||||
else if ( value==='script' ) {
|
|
||||||
return "meta.tag.script";
|
|
||||||
}
|
|
||||||
else if ( value==='style' ) {
|
|
||||||
return "meta.tag.style";
|
|
||||||
}
|
|
||||||
else if (formTags.hasOwnProperty(value.toLowerCase())) {
|
|
||||||
return "meta.tag.form";
|
|
||||||
}
|
|
||||||
else if (tableTags.hasOwnProperty(value.toLowerCase())) {
|
|
||||||
return "meta.tag.table";
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
return "meta.tag";
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
merge : true,
|
merge : true,
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue