44e95547e88732da365f803e59dce5d334251969b5a707ae12de398ce84d0fa159f297a5e71b0412d2300f26aadcf453aaba8cf4167f70d3ede8a0d51b0e69 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230
  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 './indentGuides.css';
  6. import { DynamicViewOverlay } from '../../view/dynamicViewOverlay.js';
  7. import { editorActiveIndentGuides, editorBracketHighlightingForeground1, editorBracketHighlightingForeground2, editorBracketHighlightingForeground3, editorBracketHighlightingForeground4, editorBracketHighlightingForeground5, editorBracketHighlightingForeground6, editorBracketPairGuideActiveBackground1, editorBracketPairGuideActiveBackground2, editorBracketPairGuideActiveBackground3, editorBracketPairGuideActiveBackground4, editorBracketPairGuideActiveBackground5, editorBracketPairGuideActiveBackground6, editorBracketPairGuideBackground1, editorBracketPairGuideBackground2, editorBracketPairGuideBackground3, editorBracketPairGuideBackground4, editorBracketPairGuideBackground5, editorBracketPairGuideBackground6, editorIndentGuides } from '../../../common/core/editorColorRegistry.js';
  8. import { registerThemingParticipant } from '../../../../platform/theme/common/themeService.js';
  9. import { Position } from '../../../common/core/position.js';
  10. import { ArrayQueue } from '../../../../base/common/arrays.js';
  11. import { isDefined } from '../../../../base/common/types.js';
  12. import { BracketPairGuidesClassNames } from '../../../common/model/guidesTextModelPart.js';
  13. import { IndentGuide, HorizontalGuidesState } from '../../../common/textModelGuides.js';
  14. export class IndentGuidesOverlay extends DynamicViewOverlay {
  15. constructor(context) {
  16. super();
  17. this._context = context;
  18. this._primaryPosition = null;
  19. const options = this._context.configuration.options;
  20. const wrappingInfo = options.get(134 /* EditorOption.wrappingInfo */);
  21. const fontInfo = options.get(46 /* EditorOption.fontInfo */);
  22. this._lineHeight = options.get(61 /* EditorOption.lineHeight */);
  23. this._spaceWidth = fontInfo.spaceWidth;
  24. this._maxIndentLeft = wrappingInfo.wrappingColumn === -1 ? -1 : (wrappingInfo.wrappingColumn * fontInfo.typicalHalfwidthCharacterWidth);
  25. this._bracketPairGuideOptions = options.get(13 /* EditorOption.guides */);
  26. this._renderResult = null;
  27. this._context.addEventHandler(this);
  28. }
  29. dispose() {
  30. this._context.removeEventHandler(this);
  31. this._renderResult = null;
  32. super.dispose();
  33. }
  34. // --- begin event handlers
  35. onConfigurationChanged(e) {
  36. const options = this._context.configuration.options;
  37. const wrappingInfo = options.get(134 /* EditorOption.wrappingInfo */);
  38. const fontInfo = options.get(46 /* EditorOption.fontInfo */);
  39. this._lineHeight = options.get(61 /* EditorOption.lineHeight */);
  40. this._spaceWidth = fontInfo.spaceWidth;
  41. this._maxIndentLeft = wrappingInfo.wrappingColumn === -1 ? -1 : (wrappingInfo.wrappingColumn * fontInfo.typicalHalfwidthCharacterWidth);
  42. this._bracketPairGuideOptions = options.get(13 /* EditorOption.guides */);
  43. return true;
  44. }
  45. onCursorStateChanged(e) {
  46. var _a;
  47. const selection = e.selections[0];
  48. const newPosition = selection.getPosition();
  49. if (!((_a = this._primaryPosition) === null || _a === void 0 ? void 0 : _a.equals(newPosition))) {
  50. this._primaryPosition = newPosition;
  51. return true;
  52. }
  53. return false;
  54. }
  55. onDecorationsChanged(e) {
  56. // true for inline decorations
  57. return true;
  58. }
  59. onFlushed(e) {
  60. return true;
  61. }
  62. onLinesChanged(e) {
  63. return true;
  64. }
  65. onLinesDeleted(e) {
  66. return true;
  67. }
  68. onLinesInserted(e) {
  69. return true;
  70. }
  71. onScrollChanged(e) {
  72. return e.scrollTopChanged; // || e.scrollWidthChanged;
  73. }
  74. onZonesChanged(e) {
  75. return true;
  76. }
  77. onLanguageConfigurationChanged(e) {
  78. return true;
  79. }
  80. // --- end event handlers
  81. prepareRender(ctx) {
  82. var _a, _b, _c, _d;
  83. if (!this._bracketPairGuideOptions.indentation && this._bracketPairGuideOptions.bracketPairs === false) {
  84. this._renderResult = null;
  85. return;
  86. }
  87. const visibleStartLineNumber = ctx.visibleRange.startLineNumber;
  88. const visibleEndLineNumber = ctx.visibleRange.endLineNumber;
  89. const scrollWidth = ctx.scrollWidth;
  90. const lineHeight = this._lineHeight;
  91. const activeCursorPosition = this._primaryPosition;
  92. const indents = this.getGuidesByLine(visibleStartLineNumber, visibleEndLineNumber, activeCursorPosition);
  93. const output = [];
  94. for (let lineNumber = visibleStartLineNumber; lineNumber <= visibleEndLineNumber; lineNumber++) {
  95. const lineIndex = lineNumber - visibleStartLineNumber;
  96. const indent = indents[lineIndex];
  97. let result = '';
  98. const leftOffset = (_b = (_a = ctx.visibleRangeForPosition(new Position(lineNumber, 1))) === null || _a === void 0 ? void 0 : _a.left) !== null && _b !== void 0 ? _b : 0;
  99. for (const guide of indent) {
  100. const left = guide.column === -1
  101. ? leftOffset + (guide.visibleColumn - 1) * this._spaceWidth
  102. : ctx.visibleRangeForPosition(new Position(lineNumber, guide.column)).left;
  103. if (left > scrollWidth || (this._maxIndentLeft > 0 && left > this._maxIndentLeft)) {
  104. break;
  105. }
  106. const className = guide.horizontalLine ? (guide.horizontalLine.top ? 'horizontal-top' : 'horizontal-bottom') : 'vertical';
  107. const width = guide.horizontalLine
  108. ? ((_d = (_c = ctx.visibleRangeForPosition(new Position(lineNumber, guide.horizontalLine.endColumn))) === null || _c === void 0 ? void 0 : _c.left) !== null && _d !== void 0 ? _d : (left + this._spaceWidth)) - left
  109. : this._spaceWidth;
  110. result += `<div class="core-guide ${guide.className} ${className}" style="left:${left}px;height:${lineHeight}px;width:${width}px"></div>`;
  111. }
  112. output[lineIndex] = result;
  113. }
  114. this._renderResult = output;
  115. }
  116. getGuidesByLine(visibleStartLineNumber, visibleEndLineNumber, activeCursorPosition) {
  117. const bracketGuides = this._bracketPairGuideOptions.bracketPairs !== false
  118. ? this._context.viewModel.getBracketGuidesInRangeByLine(visibleStartLineNumber, visibleEndLineNumber, activeCursorPosition, {
  119. highlightActive: this._bracketPairGuideOptions.highlightActiveBracketPair,
  120. horizontalGuides: this._bracketPairGuideOptions.bracketPairsHorizontal === true
  121. ? HorizontalGuidesState.Enabled
  122. : this._bracketPairGuideOptions.bracketPairsHorizontal === 'active'
  123. ? HorizontalGuidesState.EnabledForActive
  124. : HorizontalGuidesState.Disabled,
  125. includeInactive: this._bracketPairGuideOptions.bracketPairs === true,
  126. })
  127. : null;
  128. const indentGuides = this._bracketPairGuideOptions.indentation
  129. ? this._context.viewModel.getLinesIndentGuides(visibleStartLineNumber, visibleEndLineNumber)
  130. : null;
  131. let activeIndentStartLineNumber = 0;
  132. let activeIndentEndLineNumber = 0;
  133. let activeIndentLevel = 0;
  134. if (this._bracketPairGuideOptions.highlightActiveIndentation !== false && activeCursorPosition) {
  135. const activeIndentInfo = this._context.viewModel.getActiveIndentGuide(activeCursorPosition.lineNumber, visibleStartLineNumber, visibleEndLineNumber);
  136. activeIndentStartLineNumber = activeIndentInfo.startLineNumber;
  137. activeIndentEndLineNumber = activeIndentInfo.endLineNumber;
  138. activeIndentLevel = activeIndentInfo.indent;
  139. }
  140. const { indentSize } = this._context.viewModel.model.getOptions();
  141. const result = [];
  142. for (let lineNumber = visibleStartLineNumber; lineNumber <= visibleEndLineNumber; lineNumber++) {
  143. const lineGuides = new Array();
  144. result.push(lineGuides);
  145. const bracketGuidesInLine = bracketGuides ? bracketGuides[lineNumber - visibleStartLineNumber] : [];
  146. const bracketGuidesInLineQueue = new ArrayQueue(bracketGuidesInLine);
  147. const indentGuidesInLine = indentGuides ? indentGuides[lineNumber - visibleStartLineNumber] : [];
  148. for (let indentLvl = 1; indentLvl <= indentGuidesInLine; indentLvl++) {
  149. const indentGuide = (indentLvl - 1) * indentSize + 1;
  150. const isActive =
  151. // Disable active indent guide if there are bracket guides.
  152. (this._bracketPairGuideOptions.highlightActiveIndentation === 'always' || bracketGuidesInLine.length === 0) &&
  153. activeIndentStartLineNumber <= lineNumber &&
  154. lineNumber <= activeIndentEndLineNumber &&
  155. indentLvl === activeIndentLevel;
  156. lineGuides.push(...bracketGuidesInLineQueue.takeWhile(g => g.visibleColumn < indentGuide) || []);
  157. const peeked = bracketGuidesInLineQueue.peek();
  158. if (!peeked || peeked.visibleColumn !== indentGuide || peeked.horizontalLine) {
  159. lineGuides.push(new IndentGuide(indentGuide, -1, isActive ? 'core-guide-indent-active' : 'core-guide-indent', null, -1, -1));
  160. }
  161. }
  162. lineGuides.push(...bracketGuidesInLineQueue.takeWhile(g => true) || []);
  163. }
  164. return result;
  165. }
  166. render(startLineNumber, lineNumber) {
  167. if (!this._renderResult) {
  168. return '';
  169. }
  170. const lineIndex = lineNumber - startLineNumber;
  171. if (lineIndex < 0 || lineIndex >= this._renderResult.length) {
  172. return '';
  173. }
  174. return this._renderResult[lineIndex];
  175. }
  176. }
  177. function transparentToUndefined(color) {
  178. if (color && color.isTransparent()) {
  179. return undefined;
  180. }
  181. return color;
  182. }
  183. registerThemingParticipant((theme, collector) => {
  184. const editorIndentGuidesColor = theme.getColor(editorIndentGuides);
  185. if (editorIndentGuidesColor) {
  186. collector.addRule(`.monaco-editor .lines-content .core-guide-indent { box-shadow: 1px 0 0 0 ${editorIndentGuidesColor} inset; }`);
  187. }
  188. const editorActiveIndentGuidesColor = theme.getColor(editorActiveIndentGuides) || editorIndentGuidesColor;
  189. if (editorActiveIndentGuidesColor) {
  190. collector.addRule(`.monaco-editor .lines-content .core-guide-indent-active { box-shadow: 1px 0 0 0 ${editorActiveIndentGuidesColor} inset; }`);
  191. }
  192. const colors = [
  193. { bracketColor: editorBracketHighlightingForeground1, guideColor: editorBracketPairGuideBackground1, guideColorActive: editorBracketPairGuideActiveBackground1 },
  194. { bracketColor: editorBracketHighlightingForeground2, guideColor: editorBracketPairGuideBackground2, guideColorActive: editorBracketPairGuideActiveBackground2 },
  195. { bracketColor: editorBracketHighlightingForeground3, guideColor: editorBracketPairGuideBackground3, guideColorActive: editorBracketPairGuideActiveBackground3 },
  196. { bracketColor: editorBracketHighlightingForeground4, guideColor: editorBracketPairGuideBackground4, guideColorActive: editorBracketPairGuideActiveBackground4 },
  197. { bracketColor: editorBracketHighlightingForeground5, guideColor: editorBracketPairGuideBackground5, guideColorActive: editorBracketPairGuideActiveBackground5 },
  198. { bracketColor: editorBracketHighlightingForeground6, guideColor: editorBracketPairGuideBackground6, guideColorActive: editorBracketPairGuideActiveBackground6 }
  199. ];
  200. const colorProvider = new BracketPairGuidesClassNames();
  201. const colorValues = colors
  202. .map(c => {
  203. var _a, _b;
  204. const bracketColor = theme.getColor(c.bracketColor);
  205. const guideColor = theme.getColor(c.guideColor);
  206. const guideColorActive = theme.getColor(c.guideColorActive);
  207. const effectiveGuideColor = transparentToUndefined((_a = transparentToUndefined(guideColor)) !== null && _a !== void 0 ? _a : bracketColor === null || bracketColor === void 0 ? void 0 : bracketColor.transparent(0.3));
  208. const effectiveGuideColorActive = transparentToUndefined((_b = transparentToUndefined(guideColorActive)) !== null && _b !== void 0 ? _b : bracketColor);
  209. if (!effectiveGuideColor || !effectiveGuideColorActive) {
  210. return undefined;
  211. }
  212. return {
  213. guideColor: effectiveGuideColor,
  214. guideColorActive: effectiveGuideColorActive,
  215. };
  216. })
  217. .filter(isDefined);
  218. if (colorValues.length > 0) {
  219. for (let level = 0; level < 30; level++) {
  220. const colors = colorValues[level % colorValues.length];
  221. collector.addRule(`.monaco-editor .${colorProvider.getInlineClassNameOfLevel(level).replace(/ /g, '.')} { --guide-color: ${colors.guideColor}; --guide-color-active: ${colors.guideColorActive}; }`);
  222. }
  223. collector.addRule(`.monaco-editor .vertical { box-shadow: 1px 0 0 0 var(--guide-color) inset; }`);
  224. collector.addRule(`.monaco-editor .horizontal-top { border-top: 1px solid var(--guide-color); }`);
  225. collector.addRule(`.monaco-editor .horizontal-bottom { border-bottom: 1px solid var(--guide-color); }`);
  226. collector.addRule(`.monaco-editor .vertical.${colorProvider.activeClassName} { box-shadow: 1px 0 0 0 var(--guide-color-active) inset; }`);
  227. collector.addRule(`.monaco-editor .horizontal-top.${colorProvider.activeClassName} { border-top: 1px solid var(--guide-color-active); }`);
  228. collector.addRule(`.monaco-editor .horizontal-bottom.${colorProvider.activeClassName} { border-bottom: 1px solid var(--guide-color-active); }`);
  229. }
  230. });