83e44c3728cd12cc10dfa0c88863829fa1088427f285785bdce2ada46d1854702df1ed6b37c3149dc08ca2be19f19c11e9abe029c53a6af4ee4def7025c6f1 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363
  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 { createFastDomNode } from '../../../../base/browser/fastDomNode.js';
  6. import { Color } from '../../../../base/common/color.js';
  7. import { ViewPart } from '../../view/viewPart.js';
  8. import { Position } from '../../../common/core/position.js';
  9. import { TokenizationRegistry } from '../../../common/languages.js';
  10. import { editorCursorForeground, editorOverviewRulerBorder, editorOverviewRulerBackground } from '../../../common/core/editorColorRegistry.js';
  11. import { OverviewRulerDecorationsGroup } from '../../../common/viewModel.js';
  12. class Settings {
  13. constructor(config, theme) {
  14. const options = config.options;
  15. this.lineHeight = options.get(61 /* EditorOption.lineHeight */);
  16. this.pixelRatio = options.get(131 /* EditorOption.pixelRatio */);
  17. this.overviewRulerLanes = options.get(76 /* EditorOption.overviewRulerLanes */);
  18. this.renderBorder = options.get(75 /* EditorOption.overviewRulerBorder */);
  19. const borderColor = theme.getColor(editorOverviewRulerBorder);
  20. this.borderColor = borderColor ? borderColor.toString() : null;
  21. this.hideCursor = options.get(54 /* EditorOption.hideCursorInOverviewRuler */);
  22. const cursorColor = theme.getColor(editorCursorForeground);
  23. this.cursorColor = cursorColor ? cursorColor.transparent(0.7).toString() : null;
  24. this.themeType = theme.type;
  25. const minimapOpts = options.get(67 /* EditorOption.minimap */);
  26. const minimapEnabled = minimapOpts.enabled;
  27. const minimapSide = minimapOpts.side;
  28. const themeColor = theme.getColor(editorOverviewRulerBackground);
  29. const defaultBackground = TokenizationRegistry.getDefaultBackground();
  30. let backgroundColor = null;
  31. if (themeColor !== undefined) {
  32. backgroundColor = themeColor;
  33. }
  34. else if (minimapEnabled) {
  35. backgroundColor = defaultBackground;
  36. }
  37. if (backgroundColor === null || minimapSide === 'left') {
  38. this.backgroundColor = null;
  39. }
  40. else {
  41. this.backgroundColor = Color.Format.CSS.formatHex(backgroundColor);
  42. }
  43. const layoutInfo = options.get(133 /* EditorOption.layoutInfo */);
  44. const position = layoutInfo.overviewRuler;
  45. this.top = position.top;
  46. this.right = position.right;
  47. this.domWidth = position.width;
  48. this.domHeight = position.height;
  49. if (this.overviewRulerLanes === 0) {
  50. // overview ruler is off
  51. this.canvasWidth = 0;
  52. this.canvasHeight = 0;
  53. }
  54. else {
  55. this.canvasWidth = (this.domWidth * this.pixelRatio) | 0;
  56. this.canvasHeight = (this.domHeight * this.pixelRatio) | 0;
  57. }
  58. const [x, w] = this._initLanes(1, this.canvasWidth, this.overviewRulerLanes);
  59. this.x = x;
  60. this.w = w;
  61. }
  62. _initLanes(canvasLeftOffset, canvasWidth, laneCount) {
  63. const remainingWidth = canvasWidth - canvasLeftOffset;
  64. if (laneCount >= 3) {
  65. const leftWidth = Math.floor(remainingWidth / 3);
  66. const rightWidth = Math.floor(remainingWidth / 3);
  67. const centerWidth = remainingWidth - leftWidth - rightWidth;
  68. const leftOffset = canvasLeftOffset;
  69. const centerOffset = leftOffset + leftWidth;
  70. const rightOffset = leftOffset + leftWidth + centerWidth;
  71. return [
  72. [
  73. 0,
  74. leftOffset,
  75. centerOffset,
  76. leftOffset,
  77. rightOffset,
  78. leftOffset,
  79. centerOffset,
  80. leftOffset, // Left | Center | Right
  81. ], [
  82. 0,
  83. leftWidth,
  84. centerWidth,
  85. leftWidth + centerWidth,
  86. rightWidth,
  87. leftWidth + centerWidth + rightWidth,
  88. centerWidth + rightWidth,
  89. leftWidth + centerWidth + rightWidth, // Left | Center | Right
  90. ]
  91. ];
  92. }
  93. else if (laneCount === 2) {
  94. const leftWidth = Math.floor(remainingWidth / 2);
  95. const rightWidth = remainingWidth - leftWidth;
  96. const leftOffset = canvasLeftOffset;
  97. const rightOffset = leftOffset + leftWidth;
  98. return [
  99. [
  100. 0,
  101. leftOffset,
  102. leftOffset,
  103. leftOffset,
  104. rightOffset,
  105. leftOffset,
  106. leftOffset,
  107. leftOffset, // Left | Center | Right
  108. ], [
  109. 0,
  110. leftWidth,
  111. leftWidth,
  112. leftWidth,
  113. rightWidth,
  114. leftWidth + rightWidth,
  115. leftWidth + rightWidth,
  116. leftWidth + rightWidth, // Left | Center | Right
  117. ]
  118. ];
  119. }
  120. else {
  121. const offset = canvasLeftOffset;
  122. const width = remainingWidth;
  123. return [
  124. [
  125. 0,
  126. offset,
  127. offset,
  128. offset,
  129. offset,
  130. offset,
  131. offset,
  132. offset, // Left | Center | Right
  133. ], [
  134. 0,
  135. width,
  136. width,
  137. width,
  138. width,
  139. width,
  140. width,
  141. width, // Left | Center | Right
  142. ]
  143. ];
  144. }
  145. }
  146. equals(other) {
  147. return (this.lineHeight === other.lineHeight
  148. && this.pixelRatio === other.pixelRatio
  149. && this.overviewRulerLanes === other.overviewRulerLanes
  150. && this.renderBorder === other.renderBorder
  151. && this.borderColor === other.borderColor
  152. && this.hideCursor === other.hideCursor
  153. && this.cursorColor === other.cursorColor
  154. && this.themeType === other.themeType
  155. && this.backgroundColor === other.backgroundColor
  156. && this.top === other.top
  157. && this.right === other.right
  158. && this.domWidth === other.domWidth
  159. && this.domHeight === other.domHeight
  160. && this.canvasWidth === other.canvasWidth
  161. && this.canvasHeight === other.canvasHeight);
  162. }
  163. }
  164. export class DecorationsOverviewRuler extends ViewPart {
  165. constructor(context) {
  166. super(context);
  167. this._domNode = createFastDomNode(document.createElement('canvas'));
  168. this._domNode.setClassName('decorationsOverviewRuler');
  169. this._domNode.setPosition('absolute');
  170. this._domNode.setLayerHinting(true);
  171. this._domNode.setContain('strict');
  172. this._domNode.setAttribute('aria-hidden', 'true');
  173. this._updateSettings(false);
  174. this._tokensColorTrackerListener = TokenizationRegistry.onDidChange((e) => {
  175. if (e.changedColorMap) {
  176. this._updateSettings(true);
  177. }
  178. });
  179. this._cursorPositions = [];
  180. }
  181. dispose() {
  182. super.dispose();
  183. this._tokensColorTrackerListener.dispose();
  184. }
  185. _updateSettings(renderNow) {
  186. const newSettings = new Settings(this._context.configuration, this._context.theme);
  187. if (this._settings && this._settings.equals(newSettings)) {
  188. // nothing to do
  189. return false;
  190. }
  191. this._settings = newSettings;
  192. this._domNode.setTop(this._settings.top);
  193. this._domNode.setRight(this._settings.right);
  194. this._domNode.setWidth(this._settings.domWidth);
  195. this._domNode.setHeight(this._settings.domHeight);
  196. this._domNode.domNode.width = this._settings.canvasWidth;
  197. this._domNode.domNode.height = this._settings.canvasHeight;
  198. if (renderNow) {
  199. this._render();
  200. }
  201. return true;
  202. }
  203. // ---- begin view event handlers
  204. onConfigurationChanged(e) {
  205. return this._updateSettings(false);
  206. }
  207. onCursorStateChanged(e) {
  208. this._cursorPositions = [];
  209. for (let i = 0, len = e.selections.length; i < len; i++) {
  210. this._cursorPositions[i] = e.selections[i].getPosition();
  211. }
  212. this._cursorPositions.sort(Position.compare);
  213. return true;
  214. }
  215. onDecorationsChanged(e) {
  216. if (e.affectsOverviewRuler) {
  217. return true;
  218. }
  219. return false;
  220. }
  221. onFlushed(e) {
  222. return true;
  223. }
  224. onScrollChanged(e) {
  225. return e.scrollHeightChanged;
  226. }
  227. onZonesChanged(e) {
  228. return true;
  229. }
  230. onThemeChanged(e) {
  231. return this._updateSettings(false);
  232. }
  233. // ---- end view event handlers
  234. getDomNode() {
  235. return this._domNode.domNode;
  236. }
  237. prepareRender(ctx) {
  238. // Nothing to read
  239. }
  240. render(editorCtx) {
  241. this._render();
  242. }
  243. _render() {
  244. if (this._settings.overviewRulerLanes === 0) {
  245. // overview ruler is off
  246. this._domNode.setBackgroundColor(this._settings.backgroundColor ? this._settings.backgroundColor : '');
  247. this._domNode.setDisplay('none');
  248. return;
  249. }
  250. this._domNode.setDisplay('block');
  251. const canvasWidth = this._settings.canvasWidth;
  252. const canvasHeight = this._settings.canvasHeight;
  253. const lineHeight = this._settings.lineHeight;
  254. const viewLayout = this._context.viewLayout;
  255. const outerHeight = this._context.viewLayout.getScrollHeight();
  256. const heightRatio = canvasHeight / outerHeight;
  257. const decorations = this._context.viewModel.getAllOverviewRulerDecorations(this._context.theme);
  258. const minDecorationHeight = (6 /* Constants.MIN_DECORATION_HEIGHT */ * this._settings.pixelRatio) | 0;
  259. const halfMinDecorationHeight = (minDecorationHeight / 2) | 0;
  260. const canvasCtx = this._domNode.domNode.getContext('2d');
  261. if (this._settings.backgroundColor === null) {
  262. canvasCtx.clearRect(0, 0, canvasWidth, canvasHeight);
  263. }
  264. else {
  265. canvasCtx.fillStyle = this._settings.backgroundColor;
  266. canvasCtx.fillRect(0, 0, canvasWidth, canvasHeight);
  267. }
  268. const x = this._settings.x;
  269. const w = this._settings.w;
  270. decorations.sort(OverviewRulerDecorationsGroup.cmp);
  271. for (const decorationGroup of decorations) {
  272. const color = decorationGroup.color;
  273. const decorationGroupData = decorationGroup.data;
  274. canvasCtx.fillStyle = color;
  275. let prevLane = 0;
  276. let prevY1 = 0;
  277. let prevY2 = 0;
  278. for (let i = 0, len = decorationGroupData.length / 3; i < len; i++) {
  279. const lane = decorationGroupData[3 * i];
  280. const startLineNumber = decorationGroupData[3 * i + 1];
  281. const endLineNumber = decorationGroupData[3 * i + 2];
  282. let y1 = (viewLayout.getVerticalOffsetForLineNumber(startLineNumber) * heightRatio) | 0;
  283. let y2 = ((viewLayout.getVerticalOffsetForLineNumber(endLineNumber) + lineHeight) * heightRatio) | 0;
  284. const height = y2 - y1;
  285. if (height < minDecorationHeight) {
  286. let yCenter = ((y1 + y2) / 2) | 0;
  287. if (yCenter < halfMinDecorationHeight) {
  288. yCenter = halfMinDecorationHeight;
  289. }
  290. else if (yCenter + halfMinDecorationHeight > canvasHeight) {
  291. yCenter = canvasHeight - halfMinDecorationHeight;
  292. }
  293. y1 = yCenter - halfMinDecorationHeight;
  294. y2 = yCenter + halfMinDecorationHeight;
  295. }
  296. if (y1 > prevY2 + 1 || lane !== prevLane) {
  297. // flush prev
  298. if (i !== 0) {
  299. canvasCtx.fillRect(x[prevLane], prevY1, w[prevLane], prevY2 - prevY1);
  300. }
  301. prevLane = lane;
  302. prevY1 = y1;
  303. prevY2 = y2;
  304. }
  305. else {
  306. // merge into prev
  307. if (y2 > prevY2) {
  308. prevY2 = y2;
  309. }
  310. }
  311. }
  312. canvasCtx.fillRect(x[prevLane], prevY1, w[prevLane], prevY2 - prevY1);
  313. }
  314. // Draw cursors
  315. if (!this._settings.hideCursor && this._settings.cursorColor) {
  316. const cursorHeight = (2 * this._settings.pixelRatio) | 0;
  317. const halfCursorHeight = (cursorHeight / 2) | 0;
  318. const cursorX = this._settings.x[7 /* OverviewRulerLane.Full */];
  319. const cursorW = this._settings.w[7 /* OverviewRulerLane.Full */];
  320. canvasCtx.fillStyle = this._settings.cursorColor;
  321. let prevY1 = -100;
  322. let prevY2 = -100;
  323. for (let i = 0, len = this._cursorPositions.length; i < len; i++) {
  324. const cursor = this._cursorPositions[i];
  325. let yCenter = (viewLayout.getVerticalOffsetForLineNumber(cursor.lineNumber) * heightRatio) | 0;
  326. if (yCenter < halfCursorHeight) {
  327. yCenter = halfCursorHeight;
  328. }
  329. else if (yCenter + halfCursorHeight > canvasHeight) {
  330. yCenter = canvasHeight - halfCursorHeight;
  331. }
  332. const y1 = yCenter - halfCursorHeight;
  333. const y2 = y1 + cursorHeight;
  334. if (y1 > prevY2 + 1) {
  335. // flush prev
  336. if (i !== 0) {
  337. canvasCtx.fillRect(cursorX, prevY1, cursorW, prevY2 - prevY1);
  338. }
  339. prevY1 = y1;
  340. prevY2 = y2;
  341. }
  342. else {
  343. // merge into prev
  344. if (y2 > prevY2) {
  345. prevY2 = y2;
  346. }
  347. }
  348. }
  349. canvasCtx.fillRect(cursorX, prevY1, cursorW, prevY2 - prevY1);
  350. }
  351. if (this._settings.renderBorder && this._settings.borderColor && this._settings.overviewRulerLanes > 0) {
  352. canvasCtx.beginPath();
  353. canvasCtx.lineWidth = 1;
  354. canvasCtx.strokeStyle = this._settings.borderColor;
  355. canvasCtx.moveTo(0, 0);
  356. canvasCtx.lineTo(0, canvasHeight);
  357. canvasCtx.stroke();
  358. canvasCtx.moveTo(0, 0);
  359. canvasCtx.lineTo(canvasWidth, 0);
  360. canvasCtx.stroke();
  361. }
  362. }
  363. }