443ea9e70e5eed11df033186fd646591e7a6a86a3ab07ead70e2bb72af8eb8616197c7631db2ddc143aa152f774e30ba31e60319e232e572335fc302030b49 24 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522
  1. /*---------------------------------------------------------------------------------------------
  2. * Copyright (c) Microsoft Corporation. All rights reserved.
  3. * Licensed under the MIT License. See License.txt in the project root for license information.
  4. *--------------------------------------------------------------------------------------------*/
  5. import * as browser from '../../../../base/browser/browser.js';
  6. import { createFastDomNode } from '../../../../base/browser/fastDomNode.js';
  7. import * as platform from '../../../../base/common/platform.js';
  8. import { RangeUtil } from './rangeUtil.js';
  9. import { FloatHorizontalRange, VisibleRanges } from '../../view/renderingContext.js';
  10. import { LineDecoration } from '../../../common/viewLayout/lineDecorations.js';
  11. import { RenderLineInput, renderViewLine, LineRange, DomPosition } from '../../../common/viewLayout/viewLineRenderer.js';
  12. import { isHighContrast } from '../../../../platform/theme/common/theme.js';
  13. import { EditorFontLigatures } from '../../../common/config/editorOptions.js';
  14. const canUseFastRenderedViewLine = (function () {
  15. if (platform.isNative) {
  16. // In VSCode we know very well when the zoom level changes
  17. return true;
  18. }
  19. if (platform.isLinux || browser.isFirefox || browser.isSafari) {
  20. // On Linux, it appears that zooming affects char widths (in pixels), which is unexpected.
  21. // --
  22. // Even though we read character widths correctly, having read them at a specific zoom level
  23. // does not mean they are the same at the current zoom level.
  24. // --
  25. // This could be improved if we ever figure out how to get an event when browsers zoom,
  26. // but until then we have to stick with reading client rects.
  27. // --
  28. // The same has been observed with Firefox on Windows7
  29. // --
  30. // The same has been oversved with Safari
  31. return false;
  32. }
  33. return true;
  34. })();
  35. let monospaceAssumptionsAreValid = true;
  36. export class DomReadingContext {
  37. constructor(domNode, endNode) {
  38. this._domNode = domNode;
  39. this._clientRectDeltaLeft = 0;
  40. this._clientRectScale = 1;
  41. this._clientRectRead = false;
  42. this.endNode = endNode;
  43. }
  44. readClientRect() {
  45. if (!this._clientRectRead) {
  46. this._clientRectRead = true;
  47. const rect = this._domNode.getBoundingClientRect();
  48. this._clientRectDeltaLeft = rect.left;
  49. this._clientRectScale = rect.width / this._domNode.offsetWidth;
  50. }
  51. }
  52. get clientRectDeltaLeft() {
  53. if (!this._clientRectRead) {
  54. this.readClientRect();
  55. }
  56. return this._clientRectDeltaLeft;
  57. }
  58. get clientRectScale() {
  59. if (!this._clientRectRead) {
  60. this.readClientRect();
  61. }
  62. return this._clientRectScale;
  63. }
  64. }
  65. export class ViewLineOptions {
  66. constructor(config, themeType) {
  67. this.themeType = themeType;
  68. const options = config.options;
  69. const fontInfo = options.get(46 /* EditorOption.fontInfo */);
  70. this.renderWhitespace = options.get(90 /* EditorOption.renderWhitespace */);
  71. this.renderControlCharacters = options.get(85 /* EditorOption.renderControlCharacters */);
  72. this.spaceWidth = fontInfo.spaceWidth;
  73. this.middotWidth = fontInfo.middotWidth;
  74. this.wsmiddotWidth = fontInfo.wsmiddotWidth;
  75. this.useMonospaceOptimizations = (fontInfo.isMonospace
  76. && !options.get(29 /* EditorOption.disableMonospaceOptimizations */));
  77. this.canUseHalfwidthRightwardsArrow = fontInfo.canUseHalfwidthRightwardsArrow;
  78. this.lineHeight = options.get(61 /* EditorOption.lineHeight */);
  79. this.stopRenderingLineAfter = options.get(107 /* EditorOption.stopRenderingLineAfter */);
  80. this.fontLigatures = options.get(47 /* EditorOption.fontLigatures */);
  81. }
  82. equals(other) {
  83. return (this.themeType === other.themeType
  84. && this.renderWhitespace === other.renderWhitespace
  85. && this.renderControlCharacters === other.renderControlCharacters
  86. && this.spaceWidth === other.spaceWidth
  87. && this.middotWidth === other.middotWidth
  88. && this.wsmiddotWidth === other.wsmiddotWidth
  89. && this.useMonospaceOptimizations === other.useMonospaceOptimizations
  90. && this.canUseHalfwidthRightwardsArrow === other.canUseHalfwidthRightwardsArrow
  91. && this.lineHeight === other.lineHeight
  92. && this.stopRenderingLineAfter === other.stopRenderingLineAfter
  93. && this.fontLigatures === other.fontLigatures);
  94. }
  95. }
  96. export class ViewLine {
  97. constructor(options) {
  98. this._options = options;
  99. this._isMaybeInvalid = true;
  100. this._renderedViewLine = null;
  101. }
  102. // --- begin IVisibleLineData
  103. getDomNode() {
  104. if (this._renderedViewLine && this._renderedViewLine.domNode) {
  105. return this._renderedViewLine.domNode.domNode;
  106. }
  107. return null;
  108. }
  109. setDomNode(domNode) {
  110. if (this._renderedViewLine) {
  111. this._renderedViewLine.domNode = createFastDomNode(domNode);
  112. }
  113. else {
  114. throw new Error('I have no rendered view line to set the dom node to...');
  115. }
  116. }
  117. onContentChanged() {
  118. this._isMaybeInvalid = true;
  119. }
  120. onTokensChanged() {
  121. this._isMaybeInvalid = true;
  122. }
  123. onDecorationsChanged() {
  124. this._isMaybeInvalid = true;
  125. }
  126. onOptionsChanged(newOptions) {
  127. this._isMaybeInvalid = true;
  128. this._options = newOptions;
  129. }
  130. onSelectionChanged() {
  131. if (isHighContrast(this._options.themeType) || this._options.renderWhitespace === 'selection') {
  132. this._isMaybeInvalid = true;
  133. return true;
  134. }
  135. return false;
  136. }
  137. renderLine(lineNumber, deltaTop, viewportData, sb) {
  138. if (this._isMaybeInvalid === false) {
  139. // it appears that nothing relevant has changed
  140. return false;
  141. }
  142. this._isMaybeInvalid = false;
  143. const lineData = viewportData.getViewLineRenderingData(lineNumber);
  144. const options = this._options;
  145. const actualInlineDecorations = LineDecoration.filter(lineData.inlineDecorations, lineNumber, lineData.minColumn, lineData.maxColumn);
  146. // Only send selection information when needed for rendering whitespace
  147. let selectionsOnLine = null;
  148. if (isHighContrast(options.themeType) || this._options.renderWhitespace === 'selection') {
  149. const selections = viewportData.selections;
  150. for (const selection of selections) {
  151. if (selection.endLineNumber < lineNumber || selection.startLineNumber > lineNumber) {
  152. // Selection does not intersect line
  153. continue;
  154. }
  155. const startColumn = (selection.startLineNumber === lineNumber ? selection.startColumn : lineData.minColumn);
  156. const endColumn = (selection.endLineNumber === lineNumber ? selection.endColumn : lineData.maxColumn);
  157. if (startColumn < endColumn) {
  158. if (isHighContrast(options.themeType) || this._options.renderWhitespace !== 'selection') {
  159. actualInlineDecorations.push(new LineDecoration(startColumn, endColumn, 'inline-selected-text', 0 /* InlineDecorationType.Regular */));
  160. }
  161. else {
  162. if (!selectionsOnLine) {
  163. selectionsOnLine = [];
  164. }
  165. selectionsOnLine.push(new LineRange(startColumn - 1, endColumn - 1));
  166. }
  167. }
  168. }
  169. }
  170. const renderLineInput = new RenderLineInput(options.useMonospaceOptimizations, options.canUseHalfwidthRightwardsArrow, lineData.content, lineData.continuesWithWrappedLine, lineData.isBasicASCII, lineData.containsRTL, lineData.minColumn - 1, lineData.tokens, actualInlineDecorations, lineData.tabSize, lineData.startVisibleColumn, options.spaceWidth, options.middotWidth, options.wsmiddotWidth, options.stopRenderingLineAfter, options.renderWhitespace, options.renderControlCharacters, options.fontLigatures !== EditorFontLigatures.OFF, selectionsOnLine);
  171. if (this._renderedViewLine && this._renderedViewLine.input.equals(renderLineInput)) {
  172. // no need to do anything, we have the same render input
  173. return false;
  174. }
  175. sb.appendASCIIString('<div style="top:');
  176. sb.appendASCIIString(String(deltaTop));
  177. sb.appendASCIIString('px;height:');
  178. sb.appendASCIIString(String(this._options.lineHeight));
  179. sb.appendASCIIString('px;" class="');
  180. sb.appendASCIIString(ViewLine.CLASS_NAME);
  181. sb.appendASCIIString('">');
  182. const output = renderViewLine(renderLineInput, sb);
  183. sb.appendASCIIString('</div>');
  184. let renderedViewLine = null;
  185. if (monospaceAssumptionsAreValid && canUseFastRenderedViewLine && lineData.isBasicASCII && options.useMonospaceOptimizations && output.containsForeignElements === 0 /* ForeignElementType.None */) {
  186. if (lineData.content.length < 300 && renderLineInput.lineTokens.getCount() < 100) {
  187. // Browser rounding errors have been observed in Chrome and IE, so using the fast
  188. // view line only for short lines. Please test before removing the length check...
  189. // ---
  190. // Another rounding error has been observed on Linux in VSCode, where <span> width
  191. // rounding errors add up to an observable large number...
  192. // ---
  193. // Also see another example of rounding errors on Windows in
  194. // https://github.com/microsoft/vscode/issues/33178
  195. renderedViewLine = new FastRenderedViewLine(this._renderedViewLine ? this._renderedViewLine.domNode : null, renderLineInput, output.characterMapping);
  196. }
  197. }
  198. if (!renderedViewLine) {
  199. renderedViewLine = createRenderedLine(this._renderedViewLine ? this._renderedViewLine.domNode : null, renderLineInput, output.characterMapping, output.containsRTL, output.containsForeignElements);
  200. }
  201. this._renderedViewLine = renderedViewLine;
  202. return true;
  203. }
  204. layoutLine(lineNumber, deltaTop) {
  205. if (this._renderedViewLine && this._renderedViewLine.domNode) {
  206. this._renderedViewLine.domNode.setTop(deltaTop);
  207. this._renderedViewLine.domNode.setHeight(this._options.lineHeight);
  208. }
  209. }
  210. // --- end IVisibleLineData
  211. getWidth() {
  212. if (!this._renderedViewLine) {
  213. return 0;
  214. }
  215. return this._renderedViewLine.getWidth();
  216. }
  217. getWidthIsFast() {
  218. if (!this._renderedViewLine) {
  219. return true;
  220. }
  221. return this._renderedViewLine.getWidthIsFast();
  222. }
  223. needsMonospaceFontCheck() {
  224. if (!this._renderedViewLine) {
  225. return false;
  226. }
  227. return (this._renderedViewLine instanceof FastRenderedViewLine);
  228. }
  229. monospaceAssumptionsAreValid() {
  230. if (!this._renderedViewLine) {
  231. return monospaceAssumptionsAreValid;
  232. }
  233. if (this._renderedViewLine instanceof FastRenderedViewLine) {
  234. return this._renderedViewLine.monospaceAssumptionsAreValid();
  235. }
  236. return monospaceAssumptionsAreValid;
  237. }
  238. onMonospaceAssumptionsInvalidated() {
  239. if (this._renderedViewLine && this._renderedViewLine instanceof FastRenderedViewLine) {
  240. this._renderedViewLine = this._renderedViewLine.toSlowRenderedLine();
  241. }
  242. }
  243. getVisibleRangesForRange(lineNumber, startColumn, endColumn, context) {
  244. if (!this._renderedViewLine) {
  245. return null;
  246. }
  247. startColumn = Math.min(this._renderedViewLine.input.lineContent.length + 1, Math.max(1, startColumn));
  248. endColumn = Math.min(this._renderedViewLine.input.lineContent.length + 1, Math.max(1, endColumn));
  249. const stopRenderingLineAfter = this._renderedViewLine.input.stopRenderingLineAfter;
  250. let outsideRenderedLine = false;
  251. if (stopRenderingLineAfter !== -1 && startColumn > stopRenderingLineAfter + 1 && endColumn > stopRenderingLineAfter + 1) {
  252. // This range is obviously not visible
  253. outsideRenderedLine = true;
  254. }
  255. if (stopRenderingLineAfter !== -1 && startColumn > stopRenderingLineAfter + 1) {
  256. startColumn = stopRenderingLineAfter + 1;
  257. }
  258. if (stopRenderingLineAfter !== -1 && endColumn > stopRenderingLineAfter + 1) {
  259. endColumn = stopRenderingLineAfter + 1;
  260. }
  261. const horizontalRanges = this._renderedViewLine.getVisibleRangesForRange(lineNumber, startColumn, endColumn, context);
  262. if (horizontalRanges && horizontalRanges.length > 0) {
  263. return new VisibleRanges(outsideRenderedLine, horizontalRanges);
  264. }
  265. return null;
  266. }
  267. getColumnOfNodeOffset(lineNumber, spanNode, offset) {
  268. if (!this._renderedViewLine) {
  269. return 1;
  270. }
  271. return this._renderedViewLine.getColumnOfNodeOffset(lineNumber, spanNode, offset);
  272. }
  273. }
  274. ViewLine.CLASS_NAME = 'view-line';
  275. /**
  276. * A rendered line which is guaranteed to contain only regular ASCII and is rendered with a monospace font.
  277. */
  278. class FastRenderedViewLine {
  279. constructor(domNode, renderLineInput, characterMapping) {
  280. this.domNode = domNode;
  281. this.input = renderLineInput;
  282. this._characterMapping = characterMapping;
  283. this._charWidth = renderLineInput.spaceWidth;
  284. }
  285. getWidth() {
  286. return Math.round(this._getCharPosition(this._characterMapping.length));
  287. }
  288. getWidthIsFast() {
  289. return true;
  290. }
  291. monospaceAssumptionsAreValid() {
  292. if (!this.domNode) {
  293. return monospaceAssumptionsAreValid;
  294. }
  295. const expectedWidth = this.getWidth();
  296. const actualWidth = this.domNode.domNode.firstChild.offsetWidth;
  297. if (Math.abs(expectedWidth - actualWidth) >= 2) {
  298. // more than 2px off
  299. console.warn(`monospace assumptions have been violated, therefore disabling monospace optimizations!`);
  300. monospaceAssumptionsAreValid = false;
  301. }
  302. return monospaceAssumptionsAreValid;
  303. }
  304. toSlowRenderedLine() {
  305. return createRenderedLine(this.domNode, this.input, this._characterMapping, false, 0 /* ForeignElementType.None */);
  306. }
  307. getVisibleRangesForRange(lineNumber, startColumn, endColumn, context) {
  308. const startPosition = this._getCharPosition(startColumn);
  309. const endPosition = this._getCharPosition(endColumn);
  310. return [new FloatHorizontalRange(startPosition, endPosition - startPosition)];
  311. }
  312. _getCharPosition(column) {
  313. const horizontalOffset = this._characterMapping.getHorizontalOffset(column);
  314. return this._charWidth * horizontalOffset;
  315. }
  316. getColumnOfNodeOffset(lineNumber, spanNode, offset) {
  317. const spanNodeTextContentLength = spanNode.textContent.length;
  318. let spanIndex = -1;
  319. while (spanNode) {
  320. spanNode = spanNode.previousSibling;
  321. spanIndex++;
  322. }
  323. return this._characterMapping.getColumn(new DomPosition(spanIndex, offset), spanNodeTextContentLength);
  324. }
  325. }
  326. /**
  327. * Every time we render a line, we save what we have rendered in an instance of this class.
  328. */
  329. class RenderedViewLine {
  330. constructor(domNode, renderLineInput, characterMapping, containsRTL, containsForeignElements) {
  331. this.domNode = domNode;
  332. this.input = renderLineInput;
  333. this._characterMapping = characterMapping;
  334. this._isWhitespaceOnly = /^\s*$/.test(renderLineInput.lineContent);
  335. this._containsForeignElements = containsForeignElements;
  336. this._cachedWidth = -1;
  337. this._pixelOffsetCache = null;
  338. if (!containsRTL || this._characterMapping.length === 0 /* the line is empty */) {
  339. this._pixelOffsetCache = new Float32Array(Math.max(2, this._characterMapping.length + 1));
  340. for (let column = 0, len = this._characterMapping.length; column <= len; column++) {
  341. this._pixelOffsetCache[column] = -1;
  342. }
  343. }
  344. }
  345. // --- Reading from the DOM methods
  346. _getReadingTarget(myDomNode) {
  347. return myDomNode.domNode.firstChild;
  348. }
  349. /**
  350. * Width of the line in pixels
  351. */
  352. getWidth() {
  353. if (!this.domNode) {
  354. return 0;
  355. }
  356. if (this._cachedWidth === -1) {
  357. this._cachedWidth = this._getReadingTarget(this.domNode).offsetWidth;
  358. }
  359. return this._cachedWidth;
  360. }
  361. getWidthIsFast() {
  362. if (this._cachedWidth === -1) {
  363. return false;
  364. }
  365. return true;
  366. }
  367. /**
  368. * Visible ranges for a model range
  369. */
  370. getVisibleRangesForRange(lineNumber, startColumn, endColumn, context) {
  371. if (!this.domNode) {
  372. return null;
  373. }
  374. if (this._pixelOffsetCache !== null) {
  375. // the text is LTR
  376. const startOffset = this._readPixelOffset(this.domNode, lineNumber, startColumn, context);
  377. if (startOffset === -1) {
  378. return null;
  379. }
  380. const endOffset = this._readPixelOffset(this.domNode, lineNumber, endColumn, context);
  381. if (endOffset === -1) {
  382. return null;
  383. }
  384. return [new FloatHorizontalRange(startOffset, endOffset - startOffset)];
  385. }
  386. return this._readVisibleRangesForRange(this.domNode, lineNumber, startColumn, endColumn, context);
  387. }
  388. _readVisibleRangesForRange(domNode, lineNumber, startColumn, endColumn, context) {
  389. if (startColumn === endColumn) {
  390. const pixelOffset = this._readPixelOffset(domNode, lineNumber, startColumn, context);
  391. if (pixelOffset === -1) {
  392. return null;
  393. }
  394. else {
  395. return [new FloatHorizontalRange(pixelOffset, 0)];
  396. }
  397. }
  398. else {
  399. return this._readRawVisibleRangesForRange(domNode, startColumn, endColumn, context);
  400. }
  401. }
  402. _readPixelOffset(domNode, lineNumber, column, context) {
  403. if (this._characterMapping.length === 0) {
  404. // This line has no content
  405. if (this._containsForeignElements === 0 /* ForeignElementType.None */) {
  406. // We can assume the line is really empty
  407. return 0;
  408. }
  409. if (this._containsForeignElements === 2 /* ForeignElementType.After */) {
  410. // We have foreign elements after the (empty) line
  411. return 0;
  412. }
  413. if (this._containsForeignElements === 1 /* ForeignElementType.Before */) {
  414. // We have foreign elements before the (empty) line
  415. return this.getWidth();
  416. }
  417. // We have foreign elements before & after the (empty) line
  418. const readingTarget = this._getReadingTarget(domNode);
  419. if (readingTarget.firstChild) {
  420. return readingTarget.firstChild.offsetWidth;
  421. }
  422. else {
  423. return 0;
  424. }
  425. }
  426. if (this._pixelOffsetCache !== null) {
  427. // the text is LTR
  428. const cachedPixelOffset = this._pixelOffsetCache[column];
  429. if (cachedPixelOffset !== -1) {
  430. return cachedPixelOffset;
  431. }
  432. const result = this._actualReadPixelOffset(domNode, lineNumber, column, context);
  433. this._pixelOffsetCache[column] = result;
  434. return result;
  435. }
  436. return this._actualReadPixelOffset(domNode, lineNumber, column, context);
  437. }
  438. _actualReadPixelOffset(domNode, lineNumber, column, context) {
  439. if (this._characterMapping.length === 0) {
  440. // This line has no content
  441. const r = RangeUtil.readHorizontalRanges(this._getReadingTarget(domNode), 0, 0, 0, 0, context.clientRectDeltaLeft, context.clientRectScale, context.endNode);
  442. if (!r || r.length === 0) {
  443. return -1;
  444. }
  445. return r[0].left;
  446. }
  447. if (column === this._characterMapping.length && this._isWhitespaceOnly && this._containsForeignElements === 0 /* ForeignElementType.None */) {
  448. // This branch helps in the case of whitespace only lines which have a width set
  449. return this.getWidth();
  450. }
  451. const domPosition = this._characterMapping.getDomPosition(column);
  452. const r = RangeUtil.readHorizontalRanges(this._getReadingTarget(domNode), domPosition.partIndex, domPosition.charIndex, domPosition.partIndex, domPosition.charIndex, context.clientRectDeltaLeft, context.clientRectScale, context.endNode);
  453. if (!r || r.length === 0) {
  454. return -1;
  455. }
  456. const result = r[0].left;
  457. if (this.input.isBasicASCII) {
  458. const horizontalOffset = this._characterMapping.getHorizontalOffset(column);
  459. const expectedResult = Math.round(this.input.spaceWidth * horizontalOffset);
  460. if (Math.abs(expectedResult - result) <= 1) {
  461. return expectedResult;
  462. }
  463. }
  464. return result;
  465. }
  466. _readRawVisibleRangesForRange(domNode, startColumn, endColumn, context) {
  467. if (startColumn === 1 && endColumn === this._characterMapping.length) {
  468. // This branch helps IE with bidi text & gives a performance boost to other browsers when reading visible ranges for an entire line
  469. return [new FloatHorizontalRange(0, this.getWidth())];
  470. }
  471. const startDomPosition = this._characterMapping.getDomPosition(startColumn);
  472. const endDomPosition = this._characterMapping.getDomPosition(endColumn);
  473. return RangeUtil.readHorizontalRanges(this._getReadingTarget(domNode), startDomPosition.partIndex, startDomPosition.charIndex, endDomPosition.partIndex, endDomPosition.charIndex, context.clientRectDeltaLeft, context.clientRectScale, context.endNode);
  474. }
  475. /**
  476. * Returns the column for the text found at a specific offset inside a rendered dom node
  477. */
  478. getColumnOfNodeOffset(lineNumber, spanNode, offset) {
  479. const spanNodeTextContentLength = spanNode.textContent.length;
  480. let spanIndex = -1;
  481. while (spanNode) {
  482. spanNode = spanNode.previousSibling;
  483. spanIndex++;
  484. }
  485. return this._characterMapping.getColumn(new DomPosition(spanIndex, offset), spanNodeTextContentLength);
  486. }
  487. }
  488. class WebKitRenderedViewLine extends RenderedViewLine {
  489. _readVisibleRangesForRange(domNode, lineNumber, startColumn, endColumn, context) {
  490. const output = super._readVisibleRangesForRange(domNode, lineNumber, startColumn, endColumn, context);
  491. if (!output || output.length === 0 || startColumn === endColumn || (startColumn === 1 && endColumn === this._characterMapping.length)) {
  492. return output;
  493. }
  494. // WebKit is buggy and returns an expanded range (to contain words in some cases)
  495. // The last client rect is enlarged (I think)
  496. if (!this.input.containsRTL) {
  497. // This is an attempt to patch things up
  498. // Find position of last column
  499. const endPixelOffset = this._readPixelOffset(domNode, lineNumber, endColumn, context);
  500. if (endPixelOffset !== -1) {
  501. const lastRange = output[output.length - 1];
  502. if (lastRange.left < endPixelOffset) {
  503. // Trim down the width of the last visible range to not go after the last column's position
  504. lastRange.width = endPixelOffset - lastRange.left;
  505. }
  506. }
  507. }
  508. return output;
  509. }
  510. }
  511. const createRenderedLine = (function () {
  512. if (browser.isWebKit) {
  513. return createWebKitRenderedLine;
  514. }
  515. return createNormalRenderedLine;
  516. })();
  517. function createWebKitRenderedLine(domNode, renderLineInput, characterMapping, containsRTL, containsForeignElements) {
  518. return new WebKitRenderedViewLine(domNode, renderLineInput, characterMapping, containsRTL, containsForeignElements);
  519. }
  520. function createNormalRenderedLine(domNode, renderLineInput, characterMapping, containsRTL, containsForeignElements) {
  521. return new RenderedViewLine(domNode, renderLineInput, characterMapping, containsRTL, containsForeignElements);
  522. }