diff --git a/lib/ace/mode/behaviour/html.js b/lib/ace/mode/behaviour/html.js new file mode 100644 index 00000000..89cdedfa --- /dev/null +++ b/lib/ace/mode/behaviour/html.js @@ -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 + * + * 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: '>' + '', + selection: [1, 1] + } + } + }); +} +oop.inherits(HtmlBehaviour, XmlBehaviour); + +exports.HtmlBehaviour = HtmlBehaviour; +}); diff --git a/lib/ace/mode/behaviour/xml.js b/lib/ace/mode/behaviour/xml.js index bdfa9d82..34e8510b 100644 --- a/lib/ace/mode/behaviour/xml.js +++ b/lib/ace/mode/behaviour/xml.js @@ -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: '>' + '', + 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); diff --git a/lib/ace/mode/html.js b/lib/ace/mode/html.js index 13062a41..70a178e1 100644 --- a/lib/ace/mode/html.js +++ b/lib/ace/mode/html.js @@ -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({ diff --git a/lib/ace/mode/xml_util.js b/lib/ace/mode/xml_util.js index d76e7c1f..6aaaeb94 100644 --- a/lib/ace/mode/xml_util.js +++ b/lib/ace/mode/xml_util.js @@ -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,