From 70cda1375b294151a309c11e0cc44427f715248d Mon Sep 17 00:00:00 2001 From: Daniil Kostion Date: Mon, 2 Jul 2012 05:40:13 +1000 Subject: [PATCH 1/3] Move HTML tags out of xml_util --- lib/ace/mode/html_highlight_rules.js | 25 +++++++++++++++++--- lib/ace/mode/xml_util.js | 34 ++++------------------------ 2 files changed, 26 insertions(+), 33 deletions(-) diff --git a/lib/ace/mode/html_highlight_rules.js b/lib/ace/mode/html_highlight_rules.js index 24165fc2..4f51df77 100644 --- a/lib/ace/mode/html_highlight_rules.js +++ b/lib/ace/mode/html_highlight_rules.js @@ -44,6 +44,25 @@ var JavaScriptHighlightRules = require("./javascript_highlight_rules").JavaScrip var xmlUtil = require("./xml_util"); 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() { // regexp must not have capturing parentheses @@ -113,9 +132,9 @@ var HtmlHighlightRules = function() { } ] }; - xmlUtil.tag(this.$rules, "tag", "start"); - xmlUtil.tag(this.$rules, "style", "css-start"); - xmlUtil.tag(this.$rules, "script", "js-start"); + xmlUtil.tag(this.$rules, "tag", "start", tagMap); + xmlUtil.tag(this.$rules, "style", "css-start", tagMap); + xmlUtil.tag(this.$rules, "script", "js-start", tagMap); this.embedRules(JavaScriptHighlightRules, "js-", [{ token: "comment", diff --git a/lib/ace/mode/xml_util.js b/lib/ace/mode/xml_util.js index b58df44f..d76e7c1f 100644 --- a/lib/ace/mode/xml_util.js +++ b/lib/ace/mode/xml_util.js @@ -38,16 +38,6 @@ define(function(require, exports, module) { "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) { return [{ 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] = [{ token : "text", regex : "\\s+" @@ -89,25 +79,9 @@ exports.tag = function(states, name, nextState) { //token : "meta.tag", token : function(value) { - if ( value==='a' ) { - return "meta.tag.anchor"; - } - else if ( value==='img' ) { - 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 { + if (tagMap && tagMap[value]) { + return "meta.tag" + '.' + tagMap[value]; + } else { return "meta.tag"; } }, From 11047892178f77e8fa0f632138c46665ef8f7b5a Mon Sep 17 00:00:00 2001 From: Daniil Kostion Date: Mon, 2 Jul 2012 09:03:21 +1000 Subject: [PATCH 2/3] Add xml/html tag autoclosing behavior --- lib/ace/mode/behaviour/html.js | 98 ++++++++++++++++++++++++++++++++++ lib/ace/mode/behaviour/xml.js | 61 ++++++++++++++------- lib/ace/mode/html.js | 4 +- lib/ace/mode/xml_util.js | 4 +- 4 files changed, 143 insertions(+), 24 deletions(-) create mode 100644 lib/ace/mode/behaviour/html.js 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, From 0820419b2806ad6880c6a54ead8070daecdd5fa6 Mon Sep 17 00:00:00 2001 From: Daniil Kostion Date: Tue, 3 Jul 2012 01:35:17 +1000 Subject: [PATCH 3/3] rename variables --- lib/ace/mode/behaviour/html.js | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/lib/ace/mode/behaviour/html.js b/lib/ace/mode/behaviour/html.js index 89cdedfa..6c009ada 100644 --- a/lib/ace/mode/behaviour/html.js +++ b/lib/ace/mode/behaviour/html.js @@ -43,7 +43,7 @@ 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']; +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; @@ -78,15 +78,15 @@ var HtmlBehaviour = function () { if (!token || !hasType(token, 'meta.tag-name') || iterator.stepBackward().value.match('/')) { return } - var tag = token.value; + var element = token.value; if (atCursor){ - var tag = tag.substring(0, position.column - token.start); + var element = element.substring(0, position.column - token.start); } - if (selfClosers.indexOf(tag) !== -1){ + if (voidElements.indexOf(element) !== -1){ return; } return { - text: '>' + '', + text: '>' + '', selection: [1, 1] } }