text.js 24 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667
  1. /* ***** BEGIN LICENSE BLOCK *****
  2. * Distributed under the BSD license:
  3. *
  4. * Copyright (c) 2010, Ajax.org B.V.
  5. * All rights reserved.
  6. *
  7. * Redistribution and use in source and binary forms, with or without
  8. * modification, are permitted provided that the following conditions are met:
  9. * * Redistributions of source code must retain the above copyright
  10. * notice, this list of conditions and the following disclaimer.
  11. * * Redistributions in binary form must reproduce the above copyright
  12. * notice, this list of conditions and the following disclaimer in the
  13. * documentation and/or other materials provided with the distribution.
  14. * * Neither the name of Ajax.org B.V. nor the
  15. * names of its contributors may be used to endorse or promote products
  16. * derived from this software without specific prior written permission.
  17. *
  18. * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
  19. * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
  20. * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
  21. * DISCLAIMED. IN NO EVENT SHALL AJAX.ORG B.V. BE LIABLE FOR ANY
  22. * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
  23. * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
  24. * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
  25. * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
  26. * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
  27. * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  28. *
  29. * ***** END LICENSE BLOCK ***** */
  30. define(function(require, exports, module) {
  31. "use strict";
  32. var oop = require("../lib/oop");
  33. var dom = require("../lib/dom");
  34. var lang = require("../lib/lang");
  35. var useragent = require("../lib/useragent");
  36. var EventEmitter = require("../lib/event_emitter").EventEmitter;
  37. var Text = function(parentEl) {
  38. this.element = dom.createElement("div");
  39. this.element.className = "ace_layer ace_text-layer";
  40. parentEl.appendChild(this.element);
  41. this.$characterSize = {width: 0, height: 0};
  42. this.checkForSizeChanges();
  43. this.$pollSizeChanges();
  44. };
  45. (function() {
  46. oop.implement(this, EventEmitter);
  47. this.EOF_CHAR = "\xB6"; //"¶";
  48. this.EOL_CHAR = "\xAC"; //"¬";
  49. this.TAB_CHAR = "\u2192"; //"→" "\u21E5";
  50. this.SPACE_CHAR = "\xB7"; //"·";
  51. this.$padding = 0;
  52. this.setPadding = function(padding) {
  53. this.$padding = padding;
  54. this.element.style.padding = "0 " + padding + "px";
  55. };
  56. this.getLineHeight = function() {
  57. return this.$characterSize.height || 0;
  58. };
  59. this.getCharacterWidth = function() {
  60. return this.$characterSize.width || 0;
  61. };
  62. this.checkForSizeChanges = function() {
  63. var size = this.$measureSizes();
  64. if (size && (this.$characterSize.width !== size.width || this.$characterSize.height !== size.height)) {
  65. this.$measureNode.style.fontWeight = "bold";
  66. var boldSize = this.$measureSizes();
  67. this.$measureNode.style.fontWeight = "";
  68. this.$characterSize = size;
  69. this.allowBoldFonts = boldSize && boldSize.width === size.width && boldSize.height === size.height;
  70. this._emit("changeCharacterSize", {data: size});
  71. }
  72. };
  73. this.$pollSizeChanges = function() {
  74. var self = this;
  75. this.$pollSizeChangesTimer = setInterval(function() {
  76. self.checkForSizeChanges();
  77. }, 500);
  78. };
  79. this.$fontStyles = {
  80. fontFamily : 1,
  81. fontSize : 1,
  82. fontWeight : 1,
  83. fontStyle : 1,
  84. lineHeight : 1
  85. };
  86. this.$measureSizes = useragent.isIE || useragent.isOldGecko ? function() {
  87. var n = 1000;
  88. if (!this.$measureNode) {
  89. var measureNode = this.$measureNode = dom.createElement("div");
  90. var style = measureNode.style;
  91. style.width = style.height = "auto";
  92. style.left = style.top = (-n * 40) + "px";
  93. style.visibility = "hidden";
  94. style.position = "fixed";
  95. style.overflow = "visible";
  96. style.whiteSpace = "nowrap";
  97. // in FF 3.6 monospace fonts can have a fixed sub pixel width.
  98. // that's why we have to measure many characters
  99. // Note: characterWidth can be a float!
  100. measureNode.innerHTML = lang.stringRepeat("Xy", n);
  101. if (this.element.ownerDocument.body) {
  102. this.element.ownerDocument.body.appendChild(measureNode);
  103. } else {
  104. var container = this.element.parentNode;
  105. while (!dom.hasCssClass(container, "ace_editor"))
  106. container = container.parentNode;
  107. container.appendChild(measureNode);
  108. }
  109. }
  110. // Size and width can be null if the editor is not visible or
  111. // detached from the document
  112. if (!this.element.offsetWidth)
  113. return null;
  114. var style = this.$measureNode.style;
  115. var computedStyle = dom.computedStyle(this.element);
  116. for (var prop in this.$fontStyles)
  117. style[prop] = computedStyle[prop];
  118. var size = {
  119. height: this.$measureNode.offsetHeight,
  120. width: this.$measureNode.offsetWidth / (n * 2)
  121. };
  122. // Size and width can be null if the editor is not visible or
  123. // detached from the document
  124. if (size.width == 0 || size.height == 0)
  125. return null;
  126. return size;
  127. }
  128. : function() {
  129. if (!this.$measureNode) {
  130. var measureNode = this.$measureNode = dom.createElement("div");
  131. var style = measureNode.style;
  132. style.width = style.height = "auto";
  133. style.left = style.top = -100 + "px";
  134. style.visibility = "hidden";
  135. style.position = "fixed";
  136. style.overflow = "visible";
  137. style.whiteSpace = "nowrap";
  138. // fixes fractional fixed-width fonts; see http://git.io/CavZNw
  139. measureNode.innerHTML = lang.stringRepeat("X", 100);
  140. var container = this.element.parentNode;
  141. while (container && !dom.hasCssClass(container, "ace_editor"))
  142. container = container.parentNode;
  143. if (!container)
  144. return this.$measureNode = null;
  145. container.appendChild(measureNode);
  146. }
  147. var rect = this.$measureNode.getBoundingClientRect();
  148. var size = {
  149. height: rect.height,
  150. width: rect.width / 100
  151. };
  152. // Size and width can be null if the editor is not visible or
  153. // detached from the document
  154. if (size.width == 0 || size.height == 0)
  155. return null;
  156. return size;
  157. };
  158. this.setSession = function(session) {
  159. this.session = session;
  160. this.$computeTabString();
  161. };
  162. this.showInvisibles = false;
  163. this.setShowInvisibles = function(showInvisibles) {
  164. if (this.showInvisibles == showInvisibles)
  165. return false;
  166. this.showInvisibles = showInvisibles;
  167. this.$computeTabString();
  168. return true;
  169. };
  170. this.displayIndentGuides = true;
  171. this.setDisplayIndentGuides = function(display) {
  172. if (this.displayIndentGuides == display)
  173. return false;
  174. this.displayIndentGuides = display;
  175. this.$computeTabString();
  176. return true;
  177. };
  178. this.$tabStrings = [];
  179. this.onChangeTabSize =
  180. this.$computeTabString = function() {
  181. var tabSize = this.session.getTabSize();
  182. this.tabSize = tabSize;
  183. var tabStr = this.$tabStrings = [0];
  184. for (var i = 1; i < tabSize + 1; i++) {
  185. if (this.showInvisibles) {
  186. tabStr.push("<span class='ace_invisible'>"
  187. + this.TAB_CHAR
  188. + lang.stringRepeat("\xa0", i - 1)
  189. + "</span>");
  190. } else {
  191. tabStr.push(lang.stringRepeat("\xa0", i));
  192. }
  193. }
  194. if (this.displayIndentGuides) {
  195. this.$indentGuideRe = /\s\S| \t|\t |\s$/;
  196. var className = "ace_indent-guide";
  197. if (this.showInvisibles) {
  198. className += " ace_invisible";
  199. var spaceContent = lang.stringRepeat(this.SPACE_CHAR, this.tabSize);
  200. var tabContent = this.TAB_CHAR + lang.stringRepeat("\xa0", this.tabSize - 1);
  201. } else{
  202. var spaceContent = lang.stringRepeat("\xa0", this.tabSize);
  203. var tabContent = spaceContent;
  204. }
  205. this.$tabStrings[" "] = "<span class='" + className + "'>" + spaceContent + "</span>";
  206. this.$tabStrings["\t"] = "<span class='" + className + "'>" + tabContent + "</span>";
  207. }
  208. };
  209. this.updateLines = function(config, firstRow, lastRow) {
  210. // Due to wrap line changes there can be new lines if e.g.
  211. // the line to updated wrapped in the meantime.
  212. if (this.config.lastRow != config.lastRow ||
  213. this.config.firstRow != config.firstRow) {
  214. this.scrollLines(config);
  215. }
  216. this.config = config;
  217. var first = Math.max(firstRow, config.firstRow);
  218. var last = Math.min(lastRow, config.lastRow);
  219. var lineElements = this.element.childNodes;
  220. var lineElementsIdx = 0;
  221. for (var row = config.firstRow; row < first; row++) {
  222. var foldLine = this.session.getFoldLine(row);
  223. if (foldLine) {
  224. if (foldLine.containsRow(first)) {
  225. first = foldLine.start.row;
  226. break;
  227. } else {
  228. row = foldLine.end.row;
  229. }
  230. }
  231. lineElementsIdx ++;
  232. }
  233. var row = first;
  234. var foldLine = this.session.getNextFoldLine(row);
  235. var foldStart = foldLine ? foldLine.start.row : Infinity;
  236. while (true) {
  237. if (row > foldStart) {
  238. row = foldLine.end.row+1;
  239. foldLine = this.session.getNextFoldLine(row, foldLine);
  240. foldStart = foldLine ? foldLine.start.row :Infinity;
  241. }
  242. if (row > last)
  243. break;
  244. var lineElement = lineElements[lineElementsIdx++];
  245. if (lineElement) {
  246. var html = [];
  247. this.$renderLine(
  248. html, row, !this.$useLineGroups(), row == foldStart ? foldLine : false
  249. );
  250. lineElement.style.height = config.lineHeight * this.session.getRowLength(row) + "px";
  251. dom.setInnerHtml(lineElement, html.join(""));
  252. }
  253. row++;
  254. }
  255. };
  256. this.scrollLines = function(config) {
  257. var oldConfig = this.config;
  258. this.config = config;
  259. if (!oldConfig || oldConfig.lastRow < config.firstRow)
  260. return this.update(config);
  261. if (config.lastRow < oldConfig.firstRow)
  262. return this.update(config);
  263. var el = this.element;
  264. if (oldConfig.firstRow < config.firstRow)
  265. for (var row=this.session.getFoldedRowCount(oldConfig.firstRow, config.firstRow - 1); row>0; row--)
  266. el.removeChild(el.firstChild);
  267. if (oldConfig.lastRow > config.lastRow)
  268. for (var row=this.session.getFoldedRowCount(config.lastRow + 1, oldConfig.lastRow); row>0; row--)
  269. el.removeChild(el.lastChild);
  270. if (config.firstRow < oldConfig.firstRow) {
  271. var fragment = this.$renderLinesFragment(config, config.firstRow, oldConfig.firstRow - 1);
  272. if (el.firstChild)
  273. el.insertBefore(fragment, el.firstChild);
  274. else
  275. el.appendChild(fragment);
  276. }
  277. if (config.lastRow > oldConfig.lastRow) {
  278. var fragment = this.$renderLinesFragment(config, oldConfig.lastRow + 1, config.lastRow);
  279. el.appendChild(fragment);
  280. }
  281. };
  282. this.$renderLinesFragment = function(config, firstRow, lastRow) {
  283. var fragment = this.element.ownerDocument.createDocumentFragment();
  284. var row = firstRow;
  285. var foldLine = this.session.getNextFoldLine(row);
  286. var foldStart = foldLine ? foldLine.start.row : Infinity;
  287. while (true) {
  288. if (row > foldStart) {
  289. row = foldLine.end.row+1;
  290. foldLine = this.session.getNextFoldLine(row, foldLine);
  291. foldStart = foldLine ? foldLine.start.row : Infinity;
  292. }
  293. if (row > lastRow)
  294. break;
  295. var container = dom.createElement("div");
  296. var html = [];
  297. // Get the tokens per line as there might be some lines in between
  298. // beeing folded.
  299. this.$renderLine(html, row, false, row == foldStart ? foldLine : false);
  300. // don't use setInnerHtml since we are working with an empty DIV
  301. container.innerHTML = html.join("");
  302. if (this.$useLineGroups()) {
  303. container.className = 'ace_line_group';
  304. fragment.appendChild(container);
  305. container.style.height = config.lineHeight * this.session.getRowLength(row) + "px";
  306. } else {
  307. var lines = container.childNodes
  308. while(lines.length)
  309. fragment.appendChild(lines[0]);
  310. }
  311. row++;
  312. }
  313. return fragment;
  314. };
  315. this.update = function(config) {
  316. this.config = config;
  317. var html = [];
  318. var firstRow = config.firstRow, lastRow = config.lastRow;
  319. var row = firstRow;
  320. var foldLine = this.session.getNextFoldLine(row);
  321. var foldStart = foldLine ? foldLine.start.row : Infinity;
  322. while (true) {
  323. if (row > foldStart) {
  324. row = foldLine.end.row+1;
  325. foldLine = this.session.getNextFoldLine(row, foldLine);
  326. foldStart = foldLine ? foldLine.start.row :Infinity;
  327. }
  328. if (row > lastRow)
  329. break;
  330. if (this.$useLineGroups())
  331. html.push("<div class='ace_line_group' style='height:", config.lineHeight*this.session.getRowLength(row), "px'>")
  332. this.$renderLine(html, row, false, row == foldStart ? foldLine : false);
  333. if (this.$useLineGroups())
  334. html.push("</div>"); // end the line group
  335. row++;
  336. }
  337. this.element = dom.setInnerHtml(this.element, html.join(""));
  338. };
  339. this.$textToken = {
  340. "text": true,
  341. "rparen": true,
  342. "lparen": true
  343. };
  344. this.$renderToken = function(stringBuilder, screenColumn, token, value) {
  345. var self = this;
  346. var replaceReg = /\t|&|<|( +)|([\x00-\x1f\x80-\xa0\u1680\u180E\u2000-\u200f\u2028\u2029\u202F\u205F\u3000\uFEFF])|[\u1100-\u115F\u11A3-\u11A7\u11FA-\u11FF\u2329-\u232A\u2E80-\u2E99\u2E9B-\u2EF3\u2F00-\u2FD5\u2FF0-\u2FFB\u3000-\u303E\u3041-\u3096\u3099-\u30FF\u3105-\u312D\u3131-\u318E\u3190-\u31BA\u31C0-\u31E3\u31F0-\u321E\u3220-\u3247\u3250-\u32FE\u3300-\u4DBF\u4E00-\uA48C\uA490-\uA4C6\uA960-\uA97C\uAC00-\uD7A3\uD7B0-\uD7C6\uD7CB-\uD7FB\uF900-\uFAFF\uFE10-\uFE19\uFE30-\uFE52\uFE54-\uFE66\uFE68-\uFE6B\uFF01-\uFF60\uFFE0-\uFFE6]/g;
  347. var replaceFunc = function(c, a, b, tabIdx, idx4) {
  348. if (a) {
  349. return self.showInvisibles ?
  350. "<span class='ace_invisible'>" + lang.stringRepeat(self.SPACE_CHAR, c.length) + "</span>" :
  351. lang.stringRepeat("\xa0", c.length);
  352. } else if (c == "&") {
  353. return "&#38;";
  354. } else if (c == "<") {
  355. return "&#60;";
  356. } else if (c == "\t") {
  357. var tabSize = self.session.getScreenTabSize(screenColumn + tabIdx);
  358. screenColumn += tabSize - 1;
  359. return self.$tabStrings[tabSize];
  360. } else if (c == "\u3000") {
  361. // U+3000 is both invisible AND full-width, so must be handled uniquely
  362. var classToUse = self.showInvisibles ? "ace_cjk ace_invisible" : "ace_cjk";
  363. var space = self.showInvisibles ? self.SPACE_CHAR : "";
  364. screenColumn += 1;
  365. return "<span class='" + classToUse + "' style='width:" +
  366. (self.config.characterWidth * 2) +
  367. "px'>" + space + "</span>";
  368. } else if (b) {
  369. return "<span class='ace_invisible ace_invalid'>" + self.SPACE_CHAR + "</span>";
  370. } else {
  371. screenColumn += 1;
  372. return "<span class='ace_cjk' style='width:" +
  373. (self.config.characterWidth * 2) +
  374. "px'>" + c + "</span>";
  375. }
  376. };
  377. var output = value.replace(replaceReg, replaceFunc);
  378. if (!this.$textToken[token.type]) {
  379. var classes = "ace_" + token.type.replace(/\./g, " ace_");
  380. var style = "";
  381. if (token.type == "fold")
  382. style = " style='width:" + (token.value.length * this.config.characterWidth) + "px;' ";
  383. stringBuilder.push("<span class='", classes, "'", style, ">", output, "</span>");
  384. }
  385. else {
  386. stringBuilder.push(output);
  387. }
  388. return screenColumn + value.length;
  389. };
  390. this.renderIndentGuide = function(stringBuilder, value, max) {
  391. var cols = value.search(this.$indentGuideRe);
  392. if (cols <= 0 || cols >= max)
  393. return value;
  394. if (value[0] == " ") {
  395. cols -= cols % this.tabSize;
  396. stringBuilder.push(lang.stringRepeat(this.$tabStrings[" "], cols/this.tabSize));
  397. return value.substr(cols);
  398. } else if (value[0] == "\t") {
  399. stringBuilder.push(lang.stringRepeat(this.$tabStrings["\t"], cols));
  400. return value.substr(cols);
  401. }
  402. return value;
  403. };
  404. this.$renderWrappedLine = function(stringBuilder, tokens, splits, onlyContents) {
  405. var chars = 0;
  406. var split = 0;
  407. var splitChars = splits[0];
  408. var screenColumn = 0;
  409. for (var i = 0; i < tokens.length; i++) {
  410. var token = tokens[i];
  411. var value = token.value;
  412. if (i == 0 && this.displayIndentGuides) {
  413. chars = value.length;
  414. value = this.renderIndentGuide(stringBuilder, value, splitChars);
  415. if (!value)
  416. continue;
  417. chars -= value.length;
  418. }
  419. if (chars + value.length < splitChars) {
  420. screenColumn = this.$renderToken(stringBuilder, screenColumn, token, value);
  421. chars += value.length;
  422. } else {
  423. while (chars + value.length >= splitChars) {
  424. screenColumn = this.$renderToken(
  425. stringBuilder, screenColumn,
  426. token, value.substring(0, splitChars - chars)
  427. );
  428. value = value.substring(splitChars - chars);
  429. chars = splitChars;
  430. if (!onlyContents) {
  431. stringBuilder.push("</div>",
  432. "<div class='ace_line' style='height:",
  433. this.config.lineHeight, "px'>"
  434. );
  435. }
  436. split ++;
  437. screenColumn = 0;
  438. splitChars = splits[split] || Number.MAX_VALUE;
  439. }
  440. if (value.length != 0) {
  441. chars += value.length;
  442. screenColumn = this.$renderToken(
  443. stringBuilder, screenColumn, token, value
  444. );
  445. }
  446. }
  447. }
  448. };
  449. this.$renderSimpleLine = function(stringBuilder, tokens) {
  450. var screenColumn = 0;
  451. var token = tokens[0];
  452. var value = token.value;
  453. if (this.displayIndentGuides)
  454. value = this.renderIndentGuide(stringBuilder, value);
  455. if (value)
  456. screenColumn = this.$renderToken(stringBuilder, screenColumn, token, value);
  457. for (var i = 1; i < tokens.length; i++) {
  458. token = tokens[i];
  459. value = token.value;
  460. screenColumn = this.$renderToken(stringBuilder, screenColumn, token, value);
  461. }
  462. };
  463. // row is either first row of foldline or not in fold
  464. this.$renderLine = function(stringBuilder, row, onlyContents, foldLine) {
  465. if (!foldLine && foldLine != false)
  466. foldLine = this.session.getFoldLine(row);
  467. if (foldLine)
  468. var tokens = this.$getFoldLineTokens(row, foldLine);
  469. else
  470. var tokens = this.session.getTokens(row);
  471. if (!onlyContents) {
  472. stringBuilder.push(
  473. "<div class='ace_line' style='height:",
  474. this.config.lineHeight * (
  475. this.$useLineGroups() ? 1 :this.session.getRowLength(row)
  476. ), "px'>"
  477. );
  478. }
  479. if (tokens.length) {
  480. var splits = this.session.getRowSplitData(row);
  481. if (splits && splits.length)
  482. this.$renderWrappedLine(stringBuilder, tokens, splits, onlyContents);
  483. else
  484. this.$renderSimpleLine(stringBuilder, tokens);
  485. }
  486. if (this.showInvisibles) {
  487. if (foldLine)
  488. row = foldLine.end.row
  489. stringBuilder.push(
  490. "<span class='ace_invisible'>",
  491. row == this.session.getLength() - 1 ? this.EOF_CHAR : this.EOL_CHAR,
  492. "</span>"
  493. );
  494. }
  495. if (!onlyContents)
  496. stringBuilder.push("</div>");
  497. };
  498. this.$getFoldLineTokens = function(row, foldLine) {
  499. var session = this.session;
  500. var renderTokens = [];
  501. function addTokens(tokens, from, to) {
  502. var idx = 0, col = 0;
  503. while ((col + tokens[idx].value.length) < from) {
  504. col += tokens[idx].value.length;
  505. idx++;
  506. if (idx == tokens.length)
  507. return;
  508. }
  509. if (col != from) {
  510. var value = tokens[idx].value.substring(from - col);
  511. // Check if the token value is longer then the from...to spacing.
  512. if (value.length > (to - from))
  513. value = value.substring(0, to - from);
  514. renderTokens.push({
  515. type: tokens[idx].type,
  516. value: value
  517. });
  518. col = from + value.length;
  519. idx += 1;
  520. }
  521. while (col < to && idx < tokens.length) {
  522. var value = tokens[idx].value;
  523. if (value.length + col > to) {
  524. renderTokens.push({
  525. type: tokens[idx].type,
  526. value: value.substring(0, to - col)
  527. });
  528. } else
  529. renderTokens.push(tokens[idx]);
  530. col += value.length;
  531. idx += 1;
  532. }
  533. }
  534. var tokens = session.getTokens(row);
  535. foldLine.walk(function(placeholder, row, column, lastColumn, isNewRow) {
  536. if (placeholder != null) {
  537. renderTokens.push({
  538. type: "fold",
  539. value: placeholder
  540. });
  541. } else {
  542. if (isNewRow)
  543. tokens = session.getTokens(row);
  544. if (tokens.length)
  545. addTokens(tokens, lastColumn, column);
  546. }
  547. }, foldLine.end.row, this.session.getLine(foldLine.end.row).length);
  548. return renderTokens;
  549. };
  550. this.$useLineGroups = function() {
  551. // For the updateLines function to work correctly, it's important that the
  552. // child nodes of this.element correspond on a 1-to-1 basis to rows in the
  553. // document (as distinct from lines on the screen). For sessions that are
  554. // wrapped, this means we need to add a layer to the node hierarchy (tagged
  555. // with the class name ace_line_group).
  556. return this.session.getUseWrapMode();
  557. };
  558. this.destroy = function() {
  559. clearInterval(this.$pollSizeChangesTimer);
  560. if (this.$measureNode)
  561. this.$measureNode.parentNode.removeChild(this.$measureNode);
  562. delete this.$measureNode;
  563. };
  564. }).call(Text.prototype);
  565. exports.Text = Text;
  566. });