mode-xml.js 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608
  1. ace.define("ace/mode/xml_highlight_rules",["require","exports","module","ace/lib/oop","ace/mode/text_highlight_rules"], function(require, exports, module) {
  2. "use strict";
  3. var oop = require("../lib/oop");
  4. var TextHighlightRules = require("./text_highlight_rules").TextHighlightRules;
  5. var XmlHighlightRules = function(normalize) {
  6. this.$rules = {
  7. start : [
  8. {token : "string.cdata.xml", regex : "<\\!\\[CDATA\\[", next : "cdata"},
  9. {
  10. token : ["punctuation.xml-decl.xml", "keyword.xml-decl.xml"],
  11. regex : "(<\\?)(xml)(?=[\\s])", next : "xml_decl", caseInsensitive: true
  12. },
  13. {
  14. token : ["punctuation.instruction.xml", "keyword.instruction.xml"],
  15. regex : "(<\\?)([-_a-zA-Z0-9]+)", next : "processing_instruction",
  16. },
  17. {token : "comment.xml", regex : "<\\!--", next : "comment"},
  18. {
  19. token : ["xml-pe.doctype.xml", "xml-pe.doctype.xml"],
  20. regex : "(<\\!)(DOCTYPE)(?=[\\s])", next : "doctype", caseInsensitive: true
  21. },
  22. {include : "tag"},
  23. {token : "text.end-tag-open.xml", regex: "</"},
  24. {token : "text.tag-open.xml", regex: "<"},
  25. {include : "reference"},
  26. {defaultToken : "text.xml"}
  27. ],
  28. xml_decl : [{
  29. token : "entity.other.attribute-name.decl-attribute-name.xml",
  30. regex : "(?:[-_a-zA-Z0-9]+:)?[-_a-zA-Z0-9]+"
  31. }, {
  32. token : "keyword.operator.decl-attribute-equals.xml",
  33. regex : "="
  34. }, {
  35. include: "whitespace"
  36. }, {
  37. include: "string"
  38. }, {
  39. token : "punctuation.xml-decl.xml",
  40. regex : "\\?>",
  41. next : "start"
  42. }],
  43. processing_instruction : [
  44. {token : "punctuation.instruction.xml", regex : "\\?>", next : "start"},
  45. {defaultToken : "instruction.xml"}
  46. ],
  47. doctype : [
  48. {include : "whitespace"},
  49. {include : "string"},
  50. {token : "xml-pe.doctype.xml", regex : ">", next : "start"},
  51. {token : "xml-pe.xml", regex : "[-_a-zA-Z0-9:]+"},
  52. {token : "punctuation.int-subset", regex : "\\[", push : "int_subset"}
  53. ],
  54. int_subset : [{
  55. token : "text.xml",
  56. regex : "\\s+"
  57. }, {
  58. token: "punctuation.int-subset.xml",
  59. regex: "]",
  60. next: "pop"
  61. }, {
  62. token : ["punctuation.markup-decl.xml", "keyword.markup-decl.xml"],
  63. regex : "(<\\!)([-_a-zA-Z0-9]+)",
  64. push : [{
  65. token : "text",
  66. regex : "\\s+"
  67. },
  68. {
  69. token : "punctuation.markup-decl.xml",
  70. regex : ">",
  71. next : "pop"
  72. },
  73. {include : "string"}]
  74. }],
  75. cdata : [
  76. {token : "string.cdata.xml", regex : "\\]\\]>", next : "start"},
  77. {token : "text.xml", regex : "\\s+"},
  78. {token : "text.xml", regex : "(?:[^\\]]|\\](?!\\]>))+"}
  79. ],
  80. comment : [
  81. {token : "comment.xml", regex : "-->", next : "start"},
  82. {defaultToken : "comment.xml"}
  83. ],
  84. reference : [{
  85. token : "constant.language.escape.reference.xml",
  86. regex : "(?:&#[0-9]+;)|(?:&#x[0-9a-fA-F]+;)|(?:&[a-zA-Z0-9_:\\.-]+;)"
  87. }],
  88. attr_reference : [{
  89. token : "constant.language.escape.reference.attribute-value.xml",
  90. regex : "(?:&#[0-9]+;)|(?:&#x[0-9a-fA-F]+;)|(?:&[a-zA-Z0-9_:\\.-]+;)"
  91. }],
  92. tag : [{
  93. token : ["meta.tag.punctuation.tag-open.xml", "meta.tag.punctuation.end-tag-open.xml", "meta.tag.tag-name.xml"],
  94. regex : "(?:(<)|(</))((?:[-_a-zA-Z0-9]+:)?[-_a-zA-Z0-9]+)",
  95. next: [
  96. {include : "attributes"},
  97. {token : "meta.tag.punctuation.tag-close.xml", regex : "/?>", next : "start"}
  98. ]
  99. }],
  100. tag_whitespace : [
  101. {token : "text.tag-whitespace.xml", regex : "\\s+"}
  102. ],
  103. whitespace : [
  104. {token : "text.whitespace.xml", regex : "\\s+"}
  105. ],
  106. string: [{
  107. token : "string.xml",
  108. regex : "'",
  109. push : [
  110. {token : "string.xml", regex: "'", next: "pop"},
  111. {defaultToken : "string.xml"}
  112. ]
  113. }, {
  114. token : "string.xml",
  115. regex : '"',
  116. push : [
  117. {token : "string.xml", regex: '"', next: "pop"},
  118. {defaultToken : "string.xml"}
  119. ]
  120. }],
  121. attributes: [{
  122. token : "entity.other.attribute-name.xml",
  123. regex : "(?:[-_a-zA-Z0-9]+:)?[-_a-zA-Z0-9]+"
  124. }, {
  125. token : "keyword.operator.attribute-equals.xml",
  126. regex : "="
  127. }, {
  128. include: "tag_whitespace"
  129. }, {
  130. include: "attribute_value"
  131. }],
  132. attribute_value: [{
  133. token : "string.attribute-value.xml",
  134. regex : "'",
  135. push : [
  136. {token : "string.attribute-value.xml", regex: "'", next: "pop"},
  137. {include : "attr_reference"},
  138. {defaultToken : "string.attribute-value.xml"}
  139. ]
  140. }, {
  141. token : "string.attribute-value.xml",
  142. regex : '"',
  143. push : [
  144. {token : "string.attribute-value.xml", regex: '"', next: "pop"},
  145. {include : "attr_reference"},
  146. {defaultToken : "string.attribute-value.xml"}
  147. ]
  148. }]
  149. };
  150. if (this.constructor === XmlHighlightRules)
  151. this.normalizeRules();
  152. };
  153. (function() {
  154. this.embedTagRules = function(HighlightRules, prefix, tag){
  155. this.$rules.tag.unshift({
  156. token : ["meta.tag.punctuation.tag-open.xml", "meta.tag." + tag + ".tag-name.xml"],
  157. regex : "(<)(" + tag + "(?=\\s|>|$))",
  158. next: [
  159. {include : "attributes"},
  160. {token : "meta.tag.punctuation.tag-close.xml", regex : "/?>", next : prefix + "start"}
  161. ]
  162. });
  163. this.$rules[tag + "-end"] = [
  164. {include : "attributes"},
  165. {token : "meta.tag.punctuation.tag-close.xml", regex : "/?>", next: "start",
  166. onMatch : function(value, currentState, stack) {
  167. stack.splice(0);
  168. return this.token;
  169. }}
  170. ]
  171. this.embedRules(HighlightRules, prefix, [{
  172. token: ["meta.tag.punctuation.end-tag-open.xml", "meta.tag." + tag + ".tag-name.xml"],
  173. regex : "(</)(" + tag + "(?=\\s|>|$))",
  174. next: tag + "-end"
  175. }, {
  176. token: "string.cdata.xml",
  177. regex : "<\\!\\[CDATA\\["
  178. }, {
  179. token: "string.cdata.xml",
  180. regex : "\\]\\]>"
  181. }]);
  182. };
  183. }).call(TextHighlightRules.prototype);
  184. oop.inherits(XmlHighlightRules, TextHighlightRules);
  185. exports.XmlHighlightRules = XmlHighlightRules;
  186. });
  187. ace.define("ace/mode/behaviour/xml",["require","exports","module","ace/lib/oop","ace/mode/behaviour","ace/token_iterator"], function(require, exports, module) {
  188. "use strict";
  189. var oop = require("../../lib/oop");
  190. var Behaviour = require("../behaviour").Behaviour;
  191. var TokenIterator = require("../../token_iterator").TokenIterator;
  192. function is(token, type) {
  193. return token.type.lastIndexOf(type + ".xml") > -1;
  194. }
  195. var XmlBehaviour = function () {
  196. this.add("string_dquotes", "insertion", function (state, action, editor, session, text) {
  197. if (text == '"' || text == "'") {
  198. var quote = text;
  199. var selected = session.doc.getTextRange(editor.getSelectionRange());
  200. if (selected !== "" && selected !== "'" && selected != '"' && editor.getWrapBehavioursEnabled()) {
  201. return {
  202. text: quote + selected + quote,
  203. selection: false
  204. };
  205. }
  206. var cursor = editor.getCursorPosition();
  207. var line = session.doc.getLine(cursor.row);
  208. var rightChar = line.substring(cursor.column, cursor.column + 1);
  209. var iterator = new TokenIterator(session, cursor.row, cursor.column);
  210. var token = iterator.getCurrentToken();
  211. if (rightChar == quote && (is(token, "attribute-value") || is(token, "string"))) {
  212. return {
  213. text: "",
  214. selection: [1, 1]
  215. };
  216. }
  217. if (!token)
  218. token = iterator.stepBackward();
  219. if (!token)
  220. return;
  221. while (is(token, "tag-whitespace") || is(token, "whitespace")) {
  222. token = iterator.stepBackward();
  223. }
  224. var rightSpace = !rightChar || rightChar.match(/\s/);
  225. if (is(token, "attribute-equals") && (rightSpace || rightChar == '>') || (is(token, "decl-attribute-equals") && (rightSpace || rightChar == '?'))) {
  226. return {
  227. text: quote + quote,
  228. selection: [1, 1]
  229. };
  230. }
  231. }
  232. });
  233. this.add("string_dquotes", "deletion", function(state, action, editor, session, range) {
  234. var selected = session.doc.getTextRange(range);
  235. if (!range.isMultiLine() && (selected == '"' || selected == "'")) {
  236. var line = session.doc.getLine(range.start.row);
  237. var rightChar = line.substring(range.start.column + 1, range.start.column + 2);
  238. if (rightChar == selected) {
  239. range.end.column++;
  240. return range;
  241. }
  242. }
  243. });
  244. this.add("autoclosing", "insertion", function (state, action, editor, session, text) {
  245. if (text == '>') {
  246. var position = editor.getCursorPosition();
  247. var iterator = new TokenIterator(session, position.row, position.column);
  248. var token = iterator.getCurrentToken() || iterator.stepBackward();
  249. if (!token || !(is(token, "tag-name") || is(token, "tag-whitespace") || is(token, "attribute-name") || is(token, "attribute-equals") || is(token, "attribute-value")))
  250. return;
  251. if (is(token, "reference.attribute-value"))
  252. return;
  253. if (is(token, "attribute-value")) {
  254. var firstChar = token.value.charAt(0);
  255. if (firstChar == '"' || firstChar == "'") {
  256. var lastChar = token.value.charAt(token.value.length - 1);
  257. var tokenEnd = iterator.getCurrentTokenColumn() + token.value.length;
  258. if (tokenEnd > position.column || tokenEnd == position.column && firstChar != lastChar)
  259. return;
  260. }
  261. }
  262. while (!is(token, "tag-name")) {
  263. token = iterator.stepBackward();
  264. }
  265. var tokenRow = iterator.getCurrentTokenRow();
  266. var tokenColumn = iterator.getCurrentTokenColumn();
  267. if (is(iterator.stepBackward(), "end-tag-open"))
  268. return;
  269. var element = token.value;
  270. if (tokenRow == position.row)
  271. element = element.substring(0, position.column - tokenColumn);
  272. if (this.voidElements.hasOwnProperty(element.toLowerCase()))
  273. return;
  274. return {
  275. text: '>' + '</' + element + '>',
  276. selection: [1, 1]
  277. };
  278. }
  279. });
  280. this.add('autoindent', 'insertion', function (state, action, editor, session, text) {
  281. if (text == "\n") {
  282. var cursor = editor.getCursorPosition();
  283. var line = session.getLine(cursor.row);
  284. var rightChars = line.substring(cursor.column, cursor.column + 2);
  285. if (rightChars == '</') {
  286. var next_indent = this.$getIndent(line);
  287. var indent = next_indent + session.getTabString();
  288. return {
  289. text: '\n' + indent + '\n' + next_indent,
  290. selection: [1, indent.length, 1, indent.length]
  291. };
  292. }
  293. }
  294. });
  295. };
  296. oop.inherits(XmlBehaviour, Behaviour);
  297. exports.XmlBehaviour = XmlBehaviour;
  298. });
  299. ace.define("ace/mode/folding/xml",["require","exports","module","ace/lib/oop","ace/lib/lang","ace/range","ace/mode/folding/fold_mode","ace/token_iterator"], function(require, exports, module) {
  300. "use strict";
  301. var oop = require("../../lib/oop");
  302. var lang = require("../../lib/lang");
  303. var Range = require("../../range").Range;
  304. var BaseFoldMode = require("./fold_mode").FoldMode;
  305. var TokenIterator = require("../../token_iterator").TokenIterator;
  306. var FoldMode = exports.FoldMode = function(voidElements, optionalEndTags) {
  307. BaseFoldMode.call(this);
  308. this.voidElements = oop.mixin(voidElements || {}, optionalEndTags || {});
  309. };
  310. oop.inherits(FoldMode, BaseFoldMode);
  311. var Tag = function() {
  312. this.tagName = "";
  313. this.closing = false;
  314. this.selfClosing = false;
  315. this.start = {row: 0, column: 0};
  316. this.end = {row: 0, column: 0};
  317. };
  318. function is(token, type) {
  319. return token.type.lastIndexOf(type + ".xml") > -1;
  320. }
  321. (function() {
  322. this.getFoldWidget = function(session, foldStyle, row) {
  323. var tag = this._getFirstTagInLine(session, row);
  324. if (!tag)
  325. return "";
  326. if (tag.closing || (!tag.tagName && tag.selfClosing))
  327. return foldStyle == "markbeginend" ? "end" : "";
  328. if (!tag.tagName || tag.selfClosing || this.voidElements.hasOwnProperty(tag.tagName.toLowerCase()))
  329. return "";
  330. if (this._findEndTagInLine(session, row, tag.tagName, tag.end.column))
  331. return "";
  332. return "start";
  333. };
  334. this._getFirstTagInLine = function(session, row) {
  335. var tokens = session.getTokens(row);
  336. var tag = new Tag();
  337. for (var i = 0; i < tokens.length; i++) {
  338. var token = tokens[i];
  339. if (is(token, "tag-open")) {
  340. tag.end.column = tag.start.column + token.value.length;
  341. tag.closing = is(token, "end-tag-open");
  342. token = tokens[++i];
  343. if (!token)
  344. return null;
  345. tag.tagName = token.value;
  346. tag.end.column += token.value.length;
  347. for (i++; i < tokens.length; i++) {
  348. token = tokens[i];
  349. tag.end.column += token.value.length;
  350. if (is(token, "tag-close")) {
  351. tag.selfClosing = token.value == '/>';
  352. break;
  353. }
  354. }
  355. return tag;
  356. } else if (is(token, "tag-close")) {
  357. tag.selfClosing = token.value == '/>';
  358. return tag;
  359. }
  360. tag.start.column += token.value.length;
  361. }
  362. return null;
  363. };
  364. this._findEndTagInLine = function(session, row, tagName, startColumn) {
  365. var tokens = session.getTokens(row);
  366. var column = 0;
  367. for (var i = 0; i < tokens.length; i++) {
  368. var token = tokens[i];
  369. column += token.value.length;
  370. if (column < startColumn)
  371. continue;
  372. if (is(token, "end-tag-open")) {
  373. token = tokens[i + 1];
  374. if (token && token.value == tagName)
  375. return true;
  376. }
  377. }
  378. return false;
  379. };
  380. this._readTagForward = function(iterator) {
  381. var token = iterator.getCurrentToken();
  382. if (!token)
  383. return null;
  384. var tag = new Tag();
  385. do {
  386. if (is(token, "tag-open")) {
  387. tag.closing = is(token, "end-tag-open");
  388. tag.start.row = iterator.getCurrentTokenRow();
  389. tag.start.column = iterator.getCurrentTokenColumn();
  390. } else if (is(token, "tag-name")) {
  391. tag.tagName = token.value;
  392. } else if (is(token, "tag-close")) {
  393. tag.selfClosing = token.value == "/>";
  394. tag.end.row = iterator.getCurrentTokenRow();
  395. tag.end.column = iterator.getCurrentTokenColumn() + token.value.length;
  396. iterator.stepForward();
  397. return tag;
  398. }
  399. } while(token = iterator.stepForward());
  400. return null;
  401. };
  402. this._readTagBackward = function(iterator) {
  403. var token = iterator.getCurrentToken();
  404. if (!token)
  405. return null;
  406. var tag = new Tag();
  407. do {
  408. if (is(token, "tag-open")) {
  409. tag.closing = is(token, "end-tag-open");
  410. tag.start.row = iterator.getCurrentTokenRow();
  411. tag.start.column = iterator.getCurrentTokenColumn();
  412. iterator.stepBackward();
  413. return tag;
  414. } else if (is(token, "tag-name")) {
  415. tag.tagName = token.value;
  416. } else if (is(token, "tag-close")) {
  417. tag.selfClosing = token.value == "/>";
  418. tag.end.row = iterator.getCurrentTokenRow();
  419. tag.end.column = iterator.getCurrentTokenColumn() + token.value.length;
  420. }
  421. } while(token = iterator.stepBackward());
  422. return null;
  423. };
  424. this._pop = function(stack, tag) {
  425. while (stack.length) {
  426. var top = stack[stack.length-1];
  427. if (!tag || top.tagName == tag.tagName) {
  428. return stack.pop();
  429. }
  430. else if (this.voidElements.hasOwnProperty(tag.tagName)) {
  431. return;
  432. }
  433. else if (this.voidElements.hasOwnProperty(top.tagName)) {
  434. stack.pop();
  435. continue;
  436. } else {
  437. return null;
  438. }
  439. }
  440. };
  441. this.getFoldWidgetRange = function(session, foldStyle, row) {
  442. var firstTag = this._getFirstTagInLine(session, row);
  443. if (!firstTag)
  444. return null;
  445. var isBackward = firstTag.closing || firstTag.selfClosing;
  446. var stack = [];
  447. var tag;
  448. if (!isBackward) {
  449. var iterator = new TokenIterator(session, row, firstTag.start.column);
  450. var start = {
  451. row: row,
  452. column: firstTag.start.column + firstTag.tagName.length + 2
  453. };
  454. while (tag = this._readTagForward(iterator)) {
  455. if (tag.selfClosing) {
  456. if (!stack.length) {
  457. tag.start.column += tag.tagName.length + 2;
  458. tag.end.column -= 2;
  459. return Range.fromPoints(tag.start, tag.end);
  460. } else
  461. continue;
  462. }
  463. if (tag.closing) {
  464. this._pop(stack, tag);
  465. if (stack.length == 0)
  466. return Range.fromPoints(start, tag.start);
  467. }
  468. else {
  469. stack.push(tag);
  470. }
  471. }
  472. }
  473. else {
  474. var iterator = new TokenIterator(session, row, firstTag.end.column);
  475. var end = {
  476. row: row,
  477. column: firstTag.start.column
  478. };
  479. while (tag = this._readTagBackward(iterator)) {
  480. if (tag.selfClosing) {
  481. if (!stack.length) {
  482. tag.start.column += tag.tagName.length + 2;
  483. tag.end.column -= 2;
  484. return Range.fromPoints(tag.start, tag.end);
  485. } else
  486. continue;
  487. }
  488. if (!tag.closing) {
  489. this._pop(stack, tag);
  490. if (stack.length == 0) {
  491. tag.start.column += tag.tagName.length + 2;
  492. return Range.fromPoints(tag.start, end);
  493. }
  494. }
  495. else {
  496. stack.push(tag);
  497. }
  498. }
  499. }
  500. };
  501. }).call(FoldMode.prototype);
  502. });
  503. ace.define("ace/mode/xml",["require","exports","module","ace/lib/oop","ace/lib/lang","ace/mode/text","ace/mode/xml_highlight_rules","ace/mode/behaviour/xml","ace/mode/folding/xml"], function(require, exports, module) {
  504. "use strict";
  505. var oop = require("../lib/oop");
  506. var lang = require("../lib/lang");
  507. var TextMode = require("./text").Mode;
  508. var XmlHighlightRules = require("./xml_highlight_rules").XmlHighlightRules;
  509. var XmlBehaviour = require("./behaviour/xml").XmlBehaviour;
  510. var XmlFoldMode = require("./folding/xml").FoldMode;
  511. var Mode = function() {
  512. this.HighlightRules = XmlHighlightRules;
  513. this.$behaviour = new XmlBehaviour();
  514. this.foldingRules = new XmlFoldMode();
  515. };
  516. oop.inherits(Mode, TextMode);
  517. (function() {
  518. this.voidElements = lang.arrayToMap([]);
  519. this.blockComment = {start: "<!--", end: "-->"};
  520. this.$id = "ace/mode/xml";
  521. }).call(Mode.prototype);
  522. exports.Mode = Mode;
  523. });