mode-xml.js 23 KB

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