mode-xml.js 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665
  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.getSelectionRange().start;
  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. if (token.value == "<") {
  267. token = iterator.stepForward();
  268. break;
  269. }
  270. }
  271. var tokenRow = iterator.getCurrentTokenRow();
  272. var tokenColumn = iterator.getCurrentTokenColumn();
  273. if (is(iterator.stepBackward(), "end-tag-open"))
  274. return;
  275. var element = token.value;
  276. if (tokenRow == position.row)
  277. element = element.substring(0, position.column - tokenColumn);
  278. if (this.voidElements.hasOwnProperty(element.toLowerCase()))
  279. return;
  280. return {
  281. text: ">" + "</" + element + ">",
  282. selection: [1, 1]
  283. };
  284. }
  285. });
  286. this.add("autoindent", "insertion", function (state, action, editor, session, text) {
  287. if (text == "\n") {
  288. var cursor = editor.getCursorPosition();
  289. var line = session.getLine(cursor.row);
  290. var iterator = new TokenIterator(session, cursor.row, cursor.column);
  291. var token = iterator.getCurrentToken();
  292. if (token && token.type.indexOf("tag-close") !== -1) {
  293. if (token.value == "/>")
  294. return;
  295. while (token && token.type.indexOf("tag-name") === -1) {
  296. token = iterator.stepBackward();
  297. }
  298. if (!token) {
  299. return;
  300. }
  301. var tag = token.value;
  302. var row = iterator.getCurrentTokenRow();
  303. token = iterator.stepBackward();
  304. if (!token || token.type.indexOf("end-tag") !== -1) {
  305. return;
  306. }
  307. if (this.voidElements && !this.voidElements[tag]) {
  308. var nextToken = session.getTokenAt(cursor.row, cursor.column+1);
  309. var line = session.getLine(row);
  310. var nextIndent = this.$getIndent(line);
  311. var indent = nextIndent + session.getTabString();
  312. if (nextToken && nextToken.value === "</") {
  313. return {
  314. text: "\n" + indent + "\n" + nextIndent,
  315. selection: [1, indent.length, 1, indent.length]
  316. };
  317. } else {
  318. return {
  319. text: "\n" + indent
  320. };
  321. }
  322. }
  323. }
  324. }
  325. });
  326. };
  327. oop.inherits(XmlBehaviour, Behaviour);
  328. exports.XmlBehaviour = XmlBehaviour;
  329. });
  330. 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) {
  331. "use strict";
  332. var oop = require("../../lib/oop");
  333. var lang = require("../../lib/lang");
  334. var Range = require("../../range").Range;
  335. var BaseFoldMode = require("./fold_mode").FoldMode;
  336. var TokenIterator = require("../../token_iterator").TokenIterator;
  337. var FoldMode = exports.FoldMode = function(voidElements, optionalEndTags) {
  338. BaseFoldMode.call(this);
  339. this.voidElements = voidElements || {};
  340. this.optionalEndTags = oop.mixin({}, this.voidElements);
  341. if (optionalEndTags)
  342. oop.mixin(this.optionalEndTags, optionalEndTags);
  343. };
  344. oop.inherits(FoldMode, BaseFoldMode);
  345. var Tag = function() {
  346. this.tagName = "";
  347. this.closing = false;
  348. this.selfClosing = false;
  349. this.start = {row: 0, column: 0};
  350. this.end = {row: 0, column: 0};
  351. };
  352. function is(token, type) {
  353. return token.type.lastIndexOf(type + ".xml") > -1;
  354. }
  355. (function() {
  356. this.getFoldWidget = function(session, foldStyle, row) {
  357. var tag = this._getFirstTagInLine(session, row);
  358. if (!tag)
  359. return "";
  360. if (tag.closing || (!tag.tagName && tag.selfClosing))
  361. return foldStyle == "markbeginend" ? "end" : "";
  362. if (!tag.tagName || tag.selfClosing || this.voidElements.hasOwnProperty(tag.tagName.toLowerCase()))
  363. return "";
  364. if (this._findEndTagInLine(session, row, tag.tagName, tag.end.column))
  365. return "";
  366. return "start";
  367. };
  368. this._getFirstTagInLine = function(session, row) {
  369. var tokens = session.getTokens(row);
  370. var tag = new Tag();
  371. for (var i = 0; i < tokens.length; i++) {
  372. var token = tokens[i];
  373. if (is(token, "tag-open")) {
  374. tag.end.column = tag.start.column + token.value.length;
  375. tag.closing = is(token, "end-tag-open");
  376. token = tokens[++i];
  377. if (!token)
  378. return null;
  379. tag.tagName = token.value;
  380. tag.end.column += token.value.length;
  381. for (i++; i < tokens.length; i++) {
  382. token = tokens[i];
  383. tag.end.column += token.value.length;
  384. if (is(token, "tag-close")) {
  385. tag.selfClosing = token.value == '/>';
  386. break;
  387. }
  388. }
  389. return tag;
  390. } else if (is(token, "tag-close")) {
  391. tag.selfClosing = token.value == '/>';
  392. return tag;
  393. }
  394. tag.start.column += token.value.length;
  395. }
  396. return null;
  397. };
  398. this._findEndTagInLine = function(session, row, tagName, startColumn) {
  399. var tokens = session.getTokens(row);
  400. var column = 0;
  401. for (var i = 0; i < tokens.length; i++) {
  402. var token = tokens[i];
  403. column += token.value.length;
  404. if (column < startColumn)
  405. continue;
  406. if (is(token, "end-tag-open")) {
  407. token = tokens[i + 1];
  408. if (token && token.value == tagName)
  409. return true;
  410. }
  411. }
  412. return false;
  413. };
  414. this._readTagForward = function(iterator) {
  415. var token = iterator.getCurrentToken();
  416. if (!token)
  417. return null;
  418. var tag = new Tag();
  419. do {
  420. if (is(token, "tag-open")) {
  421. tag.closing = is(token, "end-tag-open");
  422. tag.start.row = iterator.getCurrentTokenRow();
  423. tag.start.column = iterator.getCurrentTokenColumn();
  424. } else if (is(token, "tag-name")) {
  425. tag.tagName = token.value;
  426. } else if (is(token, "tag-close")) {
  427. tag.selfClosing = token.value == "/>";
  428. tag.end.row = iterator.getCurrentTokenRow();
  429. tag.end.column = iterator.getCurrentTokenColumn() + token.value.length;
  430. iterator.stepForward();
  431. return tag;
  432. }
  433. } while(token = iterator.stepForward());
  434. return null;
  435. };
  436. this._readTagBackward = function(iterator) {
  437. var token = iterator.getCurrentToken();
  438. if (!token)
  439. return null;
  440. var tag = new Tag();
  441. do {
  442. if (is(token, "tag-open")) {
  443. tag.closing = is(token, "end-tag-open");
  444. tag.start.row = iterator.getCurrentTokenRow();
  445. tag.start.column = iterator.getCurrentTokenColumn();
  446. iterator.stepBackward();
  447. return tag;
  448. } else if (is(token, "tag-name")) {
  449. tag.tagName = token.value;
  450. } else if (is(token, "tag-close")) {
  451. tag.selfClosing = token.value == "/>";
  452. tag.end.row = iterator.getCurrentTokenRow();
  453. tag.end.column = iterator.getCurrentTokenColumn() + token.value.length;
  454. }
  455. } while(token = iterator.stepBackward());
  456. return null;
  457. };
  458. this._pop = function(stack, tag) {
  459. while (stack.length) {
  460. var top = stack[stack.length-1];
  461. if (!tag || top.tagName == tag.tagName) {
  462. return stack.pop();
  463. }
  464. else if (this.optionalEndTags.hasOwnProperty(top.tagName)) {
  465. stack.pop();
  466. continue;
  467. } else {
  468. return null;
  469. }
  470. }
  471. };
  472. this.getFoldWidgetRange = function(session, foldStyle, row) {
  473. var firstTag = this._getFirstTagInLine(session, row);
  474. if (!firstTag)
  475. return null;
  476. var isBackward = firstTag.closing || firstTag.selfClosing;
  477. var stack = [];
  478. var tag;
  479. if (!isBackward) {
  480. var iterator = new TokenIterator(session, row, firstTag.start.column);
  481. var start = {
  482. row: row,
  483. column: firstTag.start.column + firstTag.tagName.length + 2
  484. };
  485. if (firstTag.start.row == firstTag.end.row)
  486. start.column = firstTag.end.column;
  487. while (tag = this._readTagForward(iterator)) {
  488. if (tag.selfClosing) {
  489. if (!stack.length) {
  490. tag.start.column += tag.tagName.length + 2;
  491. tag.end.column -= 2;
  492. return Range.fromPoints(tag.start, tag.end);
  493. } else
  494. continue;
  495. }
  496. if (tag.closing) {
  497. this._pop(stack, tag);
  498. if (stack.length == 0)
  499. return Range.fromPoints(start, tag.start);
  500. }
  501. else {
  502. stack.push(tag);
  503. }
  504. }
  505. }
  506. else {
  507. var iterator = new TokenIterator(session, row, firstTag.end.column);
  508. var end = {
  509. row: row,
  510. column: firstTag.start.column
  511. };
  512. while (tag = this._readTagBackward(iterator)) {
  513. if (tag.selfClosing) {
  514. if (!stack.length) {
  515. tag.start.column += tag.tagName.length + 2;
  516. tag.end.column -= 2;
  517. return Range.fromPoints(tag.start, tag.end);
  518. } else
  519. continue;
  520. }
  521. if (!tag.closing) {
  522. this._pop(stack, tag);
  523. if (stack.length == 0) {
  524. tag.start.column += tag.tagName.length + 2;
  525. if (tag.start.row == tag.end.row && tag.start.column < tag.end.column)
  526. tag.start.column = tag.end.column;
  527. return Range.fromPoints(tag.start, end);
  528. }
  529. }
  530. else {
  531. stack.push(tag);
  532. }
  533. }
  534. }
  535. };
  536. }).call(FoldMode.prototype);
  537. });
  538. 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) {
  539. "use strict";
  540. var oop = require("../lib/oop");
  541. var lang = require("../lib/lang");
  542. var TextMode = require("./text").Mode;
  543. var XmlHighlightRules = require("./xml_highlight_rules").XmlHighlightRules;
  544. var XmlBehaviour = require("./behaviour/xml").XmlBehaviour;
  545. var XmlFoldMode = require("./folding/xml").FoldMode;
  546. var WorkerClient = require("../worker/worker_client").WorkerClient;
  547. var Mode = function() {
  548. this.HighlightRules = XmlHighlightRules;
  549. this.$behaviour = new XmlBehaviour();
  550. this.foldingRules = new XmlFoldMode();
  551. };
  552. oop.inherits(Mode, TextMode);
  553. (function() {
  554. this.voidElements = lang.arrayToMap([]);
  555. this.blockComment = {start: "<!--", end: "-->"};
  556. this.createWorker = function(session) {
  557. var worker = new WorkerClient(["ace"], "ace/mode/xml_worker", "Worker");
  558. worker.attachToDocument(session.getDocument());
  559. worker.on("error", function(e) {
  560. session.setAnnotations(e.data);
  561. });
  562. worker.on("terminate", function() {
  563. session.clearAnnotations();
  564. });
  565. return worker;
  566. };
  567. this.$id = "ace/mode/xml";
  568. }).call(Mode.prototype);
  569. exports.Mode = Mode;
  570. });