8a35dd1aff6260119393462f07e4d16b14eecc1024a06982d91ef9af576c11688f71f3686a92800ebc84f570c1603b53a5abf09343f25532a4b507f139180b 9.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208
  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 './lineNumbers.css';
  6. import * as platform from '../../../../base/common/platform.js';
  7. import { DynamicViewOverlay } from '../../view/dynamicViewOverlay.js';
  8. import { Position } from '../../../common/core/position.js';
  9. import { editorActiveLineNumber, editorLineNumbers } from '../../../common/core/editorColorRegistry.js';
  10. import { registerThemingParticipant } from '../../../../platform/theme/common/themeService.js';
  11. export class LineNumbersOverlay extends DynamicViewOverlay {
  12. constructor(context) {
  13. super();
  14. this._context = context;
  15. this._readConfig();
  16. this._lastCursorModelPosition = new Position(1, 1);
  17. this._lastCursorViewPosition = new Position(1, 1);
  18. this._renderResult = null;
  19. this._activeLineNumber = 1;
  20. this._context.addEventHandler(this);
  21. }
  22. _readConfig() {
  23. const options = this._context.configuration.options;
  24. this._lineHeight = options.get(61 /* EditorOption.lineHeight */);
  25. const lineNumbers = options.get(62 /* EditorOption.lineNumbers */);
  26. this._renderLineNumbers = lineNumbers.renderType;
  27. this._renderCustomLineNumbers = lineNumbers.renderFn;
  28. this._renderFinalNewline = options.get(86 /* EditorOption.renderFinalNewline */);
  29. const layoutInfo = options.get(133 /* EditorOption.layoutInfo */);
  30. this._lineNumbersLeft = layoutInfo.lineNumbersLeft;
  31. this._lineNumbersWidth = layoutInfo.lineNumbersWidth;
  32. }
  33. dispose() {
  34. this._context.removeEventHandler(this);
  35. this._renderResult = null;
  36. super.dispose();
  37. }
  38. // --- begin event handlers
  39. onConfigurationChanged(e) {
  40. this._readConfig();
  41. return true;
  42. }
  43. onCursorStateChanged(e) {
  44. const primaryViewPosition = e.selections[0].getPosition();
  45. this._lastCursorViewPosition = primaryViewPosition;
  46. this._lastCursorModelPosition = this._context.viewModel.coordinatesConverter.convertViewPositionToModelPosition(primaryViewPosition);
  47. let shouldRender = false;
  48. if (this._activeLineNumber !== primaryViewPosition.lineNumber) {
  49. this._activeLineNumber = primaryViewPosition.lineNumber;
  50. shouldRender = true;
  51. }
  52. if (this._renderLineNumbers === 2 /* RenderLineNumbersType.Relative */ || this._renderLineNumbers === 3 /* RenderLineNumbersType.Interval */) {
  53. shouldRender = true;
  54. }
  55. return shouldRender;
  56. }
  57. onFlushed(e) {
  58. return true;
  59. }
  60. onLinesChanged(e) {
  61. return true;
  62. }
  63. onLinesDeleted(e) {
  64. return true;
  65. }
  66. onLinesInserted(e) {
  67. return true;
  68. }
  69. onScrollChanged(e) {
  70. return e.scrollTopChanged;
  71. }
  72. onZonesChanged(e) {
  73. return true;
  74. }
  75. // --- end event handlers
  76. _getLineRenderLineNumber(viewLineNumber) {
  77. const modelPosition = this._context.viewModel.coordinatesConverter.convertViewPositionToModelPosition(new Position(viewLineNumber, 1));
  78. if (modelPosition.column !== 1) {
  79. return '';
  80. }
  81. const modelLineNumber = modelPosition.lineNumber;
  82. if (this._renderCustomLineNumbers) {
  83. return this._renderCustomLineNumbers(modelLineNumber);
  84. }
  85. if (this._renderLineNumbers === 3 /* RenderLineNumbersType.Interval */) {
  86. if (this._lastCursorModelPosition.lineNumber === modelLineNumber) {
  87. return String(modelLineNumber);
  88. }
  89. if (modelLineNumber % 10 === 0) {
  90. return String(modelLineNumber);
  91. }
  92. return '';
  93. }
  94. return String(modelLineNumber);
  95. }
  96. prepareRender(ctx) {
  97. if (this._renderLineNumbers === 0 /* RenderLineNumbersType.Off */) {
  98. this._renderResult = null;
  99. return;
  100. }
  101. const lineHeightClassName = (platform.isLinux ? (this._lineHeight % 2 === 0 ? ' lh-even' : ' lh-odd') : '');
  102. const visibleStartLineNumber = ctx.visibleRange.startLineNumber;
  103. const visibleEndLineNumber = ctx.visibleRange.endLineNumber;
  104. const common = '<div class="' + LineNumbersOverlay.CLASS_NAME + lineHeightClassName + '" style="left:' + this._lineNumbersLeft + 'px;width:' + this._lineNumbersWidth + 'px;">';
  105. let relativeLineNumbers = null;
  106. if (this._renderLineNumbers === 2 /* RenderLineNumbersType.Relative */) {
  107. relativeLineNumbers = new Array(visibleEndLineNumber - visibleStartLineNumber + 1);
  108. if (this._lastCursorViewPosition.lineNumber >= visibleStartLineNumber && this._lastCursorViewPosition.lineNumber <= visibleEndLineNumber) {
  109. relativeLineNumbers[this._lastCursorViewPosition.lineNumber - visibleStartLineNumber] = this._lastCursorModelPosition.lineNumber;
  110. }
  111. // Iterate up to compute relative line numbers
  112. {
  113. let value = 0;
  114. for (let lineNumber = this._lastCursorViewPosition.lineNumber + 1; lineNumber <= visibleEndLineNumber; lineNumber++) {
  115. const modelPosition = this._context.viewModel.coordinatesConverter.convertViewPositionToModelPosition(new Position(lineNumber, 1));
  116. const isWrappedLine = (modelPosition.column !== 1);
  117. if (!isWrappedLine) {
  118. value++;
  119. }
  120. if (lineNumber >= visibleStartLineNumber) {
  121. relativeLineNumbers[lineNumber - visibleStartLineNumber] = isWrappedLine ? 0 : value;
  122. }
  123. }
  124. }
  125. // Iterate down to compute relative line numbers
  126. {
  127. let value = 0;
  128. for (let lineNumber = this._lastCursorViewPosition.lineNumber - 1; lineNumber >= visibleStartLineNumber; lineNumber--) {
  129. const modelPosition = this._context.viewModel.coordinatesConverter.convertViewPositionToModelPosition(new Position(lineNumber, 1));
  130. const isWrappedLine = (modelPosition.column !== 1);
  131. if (!isWrappedLine) {
  132. value++;
  133. }
  134. if (lineNumber <= visibleEndLineNumber) {
  135. relativeLineNumbers[lineNumber - visibleStartLineNumber] = isWrappedLine ? 0 : value;
  136. }
  137. }
  138. }
  139. }
  140. const lineCount = this._context.viewModel.getLineCount();
  141. const output = [];
  142. for (let lineNumber = visibleStartLineNumber; lineNumber <= visibleEndLineNumber; lineNumber++) {
  143. const lineIndex = lineNumber - visibleStartLineNumber;
  144. if (!this._renderFinalNewline) {
  145. if (lineNumber === lineCount && this._context.viewModel.getLineLength(lineNumber) === 0) {
  146. // Do not render last (empty) line
  147. output[lineIndex] = '';
  148. continue;
  149. }
  150. }
  151. let renderLineNumber;
  152. if (relativeLineNumbers) {
  153. const relativeLineNumber = relativeLineNumbers[lineIndex];
  154. if (this._lastCursorViewPosition.lineNumber === lineNumber) {
  155. // current line!
  156. renderLineNumber = `<span class="relative-current-line-number">${relativeLineNumber}</span>`;
  157. }
  158. else if (relativeLineNumber) {
  159. renderLineNumber = String(relativeLineNumber);
  160. }
  161. else {
  162. renderLineNumber = '';
  163. }
  164. }
  165. else {
  166. renderLineNumber = this._getLineRenderLineNumber(lineNumber);
  167. }
  168. if (renderLineNumber) {
  169. if (lineNumber === this._activeLineNumber) {
  170. output[lineIndex] = ('<div class="active-line-number ' + LineNumbersOverlay.CLASS_NAME + lineHeightClassName + '" style="left:' + this._lineNumbersLeft + 'px;width:' + this._lineNumbersWidth + 'px;">'
  171. + renderLineNumber
  172. + '</div>');
  173. }
  174. else {
  175. output[lineIndex] = (common
  176. + renderLineNumber
  177. + '</div>');
  178. }
  179. }
  180. else {
  181. output[lineIndex] = '';
  182. }
  183. }
  184. this._renderResult = output;
  185. }
  186. render(startLineNumber, lineNumber) {
  187. if (!this._renderResult) {
  188. return '';
  189. }
  190. const lineIndex = lineNumber - startLineNumber;
  191. if (lineIndex < 0 || lineIndex >= this._renderResult.length) {
  192. return '';
  193. }
  194. return this._renderResult[lineIndex];
  195. }
  196. }
  197. LineNumbersOverlay.CLASS_NAME = 'line-numbers';
  198. // theming
  199. registerThemingParticipant((theme, collector) => {
  200. const lineNumbers = theme.getColor(editorLineNumbers);
  201. if (lineNumbers) {
  202. collector.addRule(`.monaco-editor .line-numbers { color: ${lineNumbers}; }`);
  203. }
  204. const activeLineNumber = theme.getColor(editorActiveLineNumber);
  205. if (activeLineNumber) {
  206. collector.addRule(`.monaco-editor .line-numbers.active-line-number { color: ${activeLineNumber}; }`);
  207. }
  208. });