viewModelImpl.js 47 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923
  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 { Color } from '../../../base/common/color.js';
  6. import { Disposable } from '../../../base/common/lifecycle.js';
  7. import * as strings from '../../../base/common/strings.js';
  8. import { EDITOR_FONT_DEFAULTS, filterValidationDecorations } from '../config/editorOptions.js';
  9. import { Position } from '../core/position.js';
  10. import { Range } from '../core/range.js';
  11. import * as textModelEvents from '../model/textModelEvents.js';
  12. import { TokenizationRegistry } from '../modes.js';
  13. import { tokenizeLineToHTML } from '../modes/textToHtmlTokenizer.js';
  14. import { MinimapTokensColorTracker } from './minimapTokensColorTracker.js';
  15. import * as viewEvents from '../view/viewEvents.js';
  16. import { ViewLayout } from '../viewLayout/viewLayout.js';
  17. import { ViewModelLinesFromModelAsIs, ViewModelLinesFromProjectedModel } from './viewModelLines.js';
  18. import { MinimapLinesRenderingData, ViewLineRenderingData, OverviewRulerDecorationsGroup } from './viewModel.js';
  19. import { ViewModelDecorations } from './viewModelDecorations.js';
  20. import { RunOnceScheduler } from '../../../base/common/async.js';
  21. import * as platform from '../../../base/common/platform.js';
  22. import { CursorsController } from '../controller/cursor.js';
  23. import { CursorConfiguration } from '../controller/cursorCommon.js';
  24. import { ViewModelEventDispatcher, FocusChangedEvent, ScrollChangedEvent, ViewZonesChangedEvent, ReadOnlyEditAttemptEvent } from './viewModelEventDispatcher.js';
  25. import { PLAINTEXT_MODE_ID } from '../modes/modesRegistry.js';
  26. import { ArrayQueue } from '../../../base/common/arrays.js';
  27. const USE_IDENTITY_LINES_COLLECTION = true;
  28. export class ViewModel extends Disposable {
  29. constructor(editorId, configuration, model, domLineBreaksComputerFactory, monospaceLineBreaksComputerFactory, scheduleAtNextAnimationFrame) {
  30. super();
  31. this._editorId = editorId;
  32. this._configuration = configuration;
  33. this.model = model;
  34. this._eventDispatcher = new ViewModelEventDispatcher();
  35. this.onEvent = this._eventDispatcher.onEvent;
  36. this.cursorConfig = new CursorConfiguration(this.model.getLanguageId(), this.model.getOptions(), this._configuration);
  37. this._tokenizeViewportSoon = this._register(new RunOnceScheduler(() => this.tokenizeViewport(), 50));
  38. this._updateConfigurationViewLineCount = this._register(new RunOnceScheduler(() => this._updateConfigurationViewLineCountNow(), 0));
  39. this._hasFocus = false;
  40. this._viewportStartLine = -1;
  41. this._viewportStartLineTrackedRange = null;
  42. this._viewportStartLineDelta = 0;
  43. if (USE_IDENTITY_LINES_COLLECTION && this.model.isTooLargeForTokenization()) {
  44. this._lines = new ViewModelLinesFromModelAsIs(this.model);
  45. }
  46. else {
  47. const options = this._configuration.options;
  48. const fontInfo = options.get(43 /* fontInfo */);
  49. const wrappingStrategy = options.get(124 /* wrappingStrategy */);
  50. const wrappingInfo = options.get(131 /* wrappingInfo */);
  51. const wrappingIndent = options.get(123 /* wrappingIndent */);
  52. this._lines = new ViewModelLinesFromProjectedModel(this._editorId, this.model, domLineBreaksComputerFactory, monospaceLineBreaksComputerFactory, fontInfo, this.model.getOptions().tabSize, wrappingStrategy, wrappingInfo.wrappingColumn, wrappingIndent);
  53. }
  54. this.coordinatesConverter = this._lines.createCoordinatesConverter();
  55. this._cursor = this._register(new CursorsController(model, this, this.coordinatesConverter, this.cursorConfig));
  56. this.viewLayout = this._register(new ViewLayout(this._configuration, this.getLineCount(), scheduleAtNextAnimationFrame));
  57. this._register(this.viewLayout.onDidScroll((e) => {
  58. if (e.scrollTopChanged) {
  59. this._tokenizeViewportSoon.schedule();
  60. }
  61. this._eventDispatcher.emitSingleViewEvent(new viewEvents.ViewScrollChangedEvent(e));
  62. this._eventDispatcher.emitOutgoingEvent(new ScrollChangedEvent(e.oldScrollWidth, e.oldScrollLeft, e.oldScrollHeight, e.oldScrollTop, e.scrollWidth, e.scrollLeft, e.scrollHeight, e.scrollTop));
  63. }));
  64. this._register(this.viewLayout.onDidContentSizeChange((e) => {
  65. this._eventDispatcher.emitOutgoingEvent(e);
  66. }));
  67. this._decorations = new ViewModelDecorations(this._editorId, this.model, this._configuration, this._lines, this.coordinatesConverter);
  68. this._registerModelEvents();
  69. this._register(this._configuration.onDidChangeFast((e) => {
  70. try {
  71. const eventsCollector = this._eventDispatcher.beginEmitViewEvents();
  72. this._onConfigurationChanged(eventsCollector, e);
  73. }
  74. finally {
  75. this._eventDispatcher.endEmitViewEvents();
  76. }
  77. }));
  78. this._register(MinimapTokensColorTracker.getInstance().onDidChange(() => {
  79. this._eventDispatcher.emitSingleViewEvent(new viewEvents.ViewTokensColorsChangedEvent());
  80. }));
  81. this._updateConfigurationViewLineCountNow();
  82. }
  83. dispose() {
  84. // First remove listeners, as disposing the lines might end up sending
  85. // model decoration changed events ... and we no longer care about them ...
  86. super.dispose();
  87. this._decorations.dispose();
  88. this._lines.dispose();
  89. this.invalidateMinimapColorCache();
  90. this._viewportStartLineTrackedRange = this.model._setTrackedRange(this._viewportStartLineTrackedRange, null, 1 /* NeverGrowsWhenTypingAtEdges */);
  91. this._eventDispatcher.dispose();
  92. }
  93. createLineBreaksComputer() {
  94. return this._lines.createLineBreaksComputer();
  95. }
  96. addViewEventHandler(eventHandler) {
  97. this._eventDispatcher.addViewEventHandler(eventHandler);
  98. }
  99. removeViewEventHandler(eventHandler) {
  100. this._eventDispatcher.removeViewEventHandler(eventHandler);
  101. }
  102. _updateConfigurationViewLineCountNow() {
  103. this._configuration.setViewLineCount(this._lines.getViewLineCount());
  104. }
  105. tokenizeViewport() {
  106. const linesViewportData = this.viewLayout.getLinesViewportData();
  107. const viewVisibleRange = new Range(linesViewportData.startLineNumber, this.getLineMinColumn(linesViewportData.startLineNumber), linesViewportData.endLineNumber, this.getLineMaxColumn(linesViewportData.endLineNumber));
  108. const modelVisibleRanges = this._toModelVisibleRanges(viewVisibleRange);
  109. for (const modelVisibleRange of modelVisibleRanges) {
  110. this.model.tokenizeViewport(modelVisibleRange.startLineNumber, modelVisibleRange.endLineNumber);
  111. }
  112. }
  113. setHasFocus(hasFocus) {
  114. this._hasFocus = hasFocus;
  115. this._cursor.setHasFocus(hasFocus);
  116. this._eventDispatcher.emitSingleViewEvent(new viewEvents.ViewFocusChangedEvent(hasFocus));
  117. this._eventDispatcher.emitOutgoingEvent(new FocusChangedEvent(!hasFocus, hasFocus));
  118. }
  119. onCompositionStart() {
  120. this._eventDispatcher.emitSingleViewEvent(new viewEvents.ViewCompositionStartEvent());
  121. }
  122. onCompositionEnd() {
  123. this._eventDispatcher.emitSingleViewEvent(new viewEvents.ViewCompositionEndEvent());
  124. }
  125. onDidColorThemeChange() {
  126. this._eventDispatcher.emitSingleViewEvent(new viewEvents.ViewThemeChangedEvent());
  127. }
  128. _onConfigurationChanged(eventsCollector, e) {
  129. // We might need to restore the current centered view range, so save it (if available)
  130. let previousViewportStartModelPosition = null;
  131. if (this._viewportStartLine !== -1) {
  132. let previousViewportStartViewPosition = new Position(this._viewportStartLine, this.getLineMinColumn(this._viewportStartLine));
  133. previousViewportStartModelPosition = this.coordinatesConverter.convertViewPositionToModelPosition(previousViewportStartViewPosition);
  134. }
  135. let restorePreviousViewportStart = false;
  136. const options = this._configuration.options;
  137. const fontInfo = options.get(43 /* fontInfo */);
  138. const wrappingStrategy = options.get(124 /* wrappingStrategy */);
  139. const wrappingInfo = options.get(131 /* wrappingInfo */);
  140. const wrappingIndent = options.get(123 /* wrappingIndent */);
  141. if (this._lines.setWrappingSettings(fontInfo, wrappingStrategy, wrappingInfo.wrappingColumn, wrappingIndent)) {
  142. eventsCollector.emitViewEvent(new viewEvents.ViewFlushedEvent());
  143. eventsCollector.emitViewEvent(new viewEvents.ViewLineMappingChangedEvent());
  144. eventsCollector.emitViewEvent(new viewEvents.ViewDecorationsChangedEvent(null));
  145. this._cursor.onLineMappingChanged(eventsCollector);
  146. this._decorations.onLineMappingChanged();
  147. this.viewLayout.onFlushed(this.getLineCount());
  148. if (this.viewLayout.getCurrentScrollTop() !== 0) {
  149. // Never change the scroll position from 0 to something else...
  150. restorePreviousViewportStart = true;
  151. }
  152. this._updateConfigurationViewLineCount.schedule();
  153. }
  154. if (e.hasChanged(80 /* readOnly */)) {
  155. // Must read again all decorations due to readOnly filtering
  156. this._decorations.reset();
  157. eventsCollector.emitViewEvent(new viewEvents.ViewDecorationsChangedEvent(null));
  158. }
  159. eventsCollector.emitViewEvent(new viewEvents.ViewConfigurationChangedEvent(e));
  160. this.viewLayout.onConfigurationChanged(e);
  161. if (restorePreviousViewportStart && previousViewportStartModelPosition) {
  162. const viewPosition = this.coordinatesConverter.convertModelPositionToViewPosition(previousViewportStartModelPosition);
  163. const viewPositionTop = this.viewLayout.getVerticalOffsetForLineNumber(viewPosition.lineNumber);
  164. this.viewLayout.setScrollPosition({ scrollTop: viewPositionTop + this._viewportStartLineDelta }, 1 /* Immediate */);
  165. }
  166. if (CursorConfiguration.shouldRecreate(e)) {
  167. this.cursorConfig = new CursorConfiguration(this.model.getLanguageId(), this.model.getOptions(), this._configuration);
  168. this._cursor.updateConfiguration(this.cursorConfig);
  169. }
  170. }
  171. _registerModelEvents() {
  172. this._register(this.model.onDidChangeContentOrInjectedText((e) => {
  173. try {
  174. const eventsCollector = this._eventDispatcher.beginEmitViewEvents();
  175. let hadOtherModelChange = false;
  176. let hadModelLineChangeThatChangedLineMapping = false;
  177. const changes = e.changes;
  178. const versionId = (e instanceof textModelEvents.ModelRawContentChangedEvent ? e.versionId : null);
  179. // Do a first pass to compute line mappings, and a second pass to actually interpret them
  180. const lineBreaksComputer = this._lines.createLineBreaksComputer();
  181. for (const change of changes) {
  182. switch (change.changeType) {
  183. case 4 /* LinesInserted */: {
  184. for (let lineIdx = 0; lineIdx < change.detail.length; lineIdx++) {
  185. const line = change.detail[lineIdx];
  186. let injectedText = change.injectedTexts[lineIdx];
  187. if (injectedText) {
  188. injectedText = injectedText.filter(element => (!element.ownerId || element.ownerId === this._editorId));
  189. }
  190. lineBreaksComputer.addRequest(line, injectedText, null);
  191. }
  192. break;
  193. }
  194. case 2 /* LineChanged */: {
  195. let injectedText = null;
  196. if (change.injectedText) {
  197. injectedText = change.injectedText.filter(element => (!element.ownerId || element.ownerId === this._editorId));
  198. }
  199. lineBreaksComputer.addRequest(change.detail, injectedText, null);
  200. break;
  201. }
  202. }
  203. }
  204. const lineBreaks = lineBreaksComputer.finalize();
  205. const lineBreakQueue = new ArrayQueue(lineBreaks);
  206. for (const change of changes) {
  207. switch (change.changeType) {
  208. case 1 /* Flush */: {
  209. this._lines.onModelFlushed();
  210. eventsCollector.emitViewEvent(new viewEvents.ViewFlushedEvent());
  211. this._decorations.reset();
  212. this.viewLayout.onFlushed(this.getLineCount());
  213. hadOtherModelChange = true;
  214. break;
  215. }
  216. case 3 /* LinesDeleted */: {
  217. const linesDeletedEvent = this._lines.onModelLinesDeleted(versionId, change.fromLineNumber, change.toLineNumber);
  218. if (linesDeletedEvent !== null) {
  219. eventsCollector.emitViewEvent(linesDeletedEvent);
  220. this.viewLayout.onLinesDeleted(linesDeletedEvent.fromLineNumber, linesDeletedEvent.toLineNumber);
  221. }
  222. hadOtherModelChange = true;
  223. break;
  224. }
  225. case 4 /* LinesInserted */: {
  226. const insertedLineBreaks = lineBreakQueue.takeCount(change.detail.length);
  227. const linesInsertedEvent = this._lines.onModelLinesInserted(versionId, change.fromLineNumber, change.toLineNumber, insertedLineBreaks);
  228. if (linesInsertedEvent !== null) {
  229. eventsCollector.emitViewEvent(linesInsertedEvent);
  230. this.viewLayout.onLinesInserted(linesInsertedEvent.fromLineNumber, linesInsertedEvent.toLineNumber);
  231. }
  232. hadOtherModelChange = true;
  233. break;
  234. }
  235. case 2 /* LineChanged */: {
  236. const changedLineBreakData = lineBreakQueue.dequeue();
  237. const [lineMappingChanged, linesChangedEvent, linesInsertedEvent, linesDeletedEvent] = this._lines.onModelLineChanged(versionId, change.lineNumber, changedLineBreakData);
  238. hadModelLineChangeThatChangedLineMapping = lineMappingChanged;
  239. if (linesChangedEvent) {
  240. eventsCollector.emitViewEvent(linesChangedEvent);
  241. }
  242. if (linesInsertedEvent) {
  243. eventsCollector.emitViewEvent(linesInsertedEvent);
  244. this.viewLayout.onLinesInserted(linesInsertedEvent.fromLineNumber, linesInsertedEvent.toLineNumber);
  245. }
  246. if (linesDeletedEvent) {
  247. eventsCollector.emitViewEvent(linesDeletedEvent);
  248. this.viewLayout.onLinesDeleted(linesDeletedEvent.fromLineNumber, linesDeletedEvent.toLineNumber);
  249. }
  250. break;
  251. }
  252. case 5 /* EOLChanged */: {
  253. // Nothing to do. The new version will be accepted below
  254. break;
  255. }
  256. }
  257. }
  258. if (versionId !== null) {
  259. this._lines.acceptVersionId(versionId);
  260. }
  261. this.viewLayout.onHeightMaybeChanged();
  262. if (!hadOtherModelChange && hadModelLineChangeThatChangedLineMapping) {
  263. eventsCollector.emitViewEvent(new viewEvents.ViewLineMappingChangedEvent());
  264. eventsCollector.emitViewEvent(new viewEvents.ViewDecorationsChangedEvent(null));
  265. this._cursor.onLineMappingChanged(eventsCollector);
  266. this._decorations.onLineMappingChanged();
  267. }
  268. }
  269. finally {
  270. this._eventDispatcher.endEmitViewEvents();
  271. }
  272. // Update the configuration and reset the centered view line
  273. this._viewportStartLine = -1;
  274. this._configuration.setMaxLineNumber(this.model.getLineCount());
  275. this._updateConfigurationViewLineCountNow();
  276. // Recover viewport
  277. if (!this._hasFocus && this.model.getAttachedEditorCount() >= 2 && this._viewportStartLineTrackedRange) {
  278. const modelRange = this.model._getTrackedRange(this._viewportStartLineTrackedRange);
  279. if (modelRange) {
  280. const viewPosition = this.coordinatesConverter.convertModelPositionToViewPosition(modelRange.getStartPosition());
  281. const viewPositionTop = this.viewLayout.getVerticalOffsetForLineNumber(viewPosition.lineNumber);
  282. this.viewLayout.setScrollPosition({ scrollTop: viewPositionTop + this._viewportStartLineDelta }, 1 /* Immediate */);
  283. }
  284. }
  285. try {
  286. const eventsCollector = this._eventDispatcher.beginEmitViewEvents();
  287. this._cursor.onModelContentChanged(eventsCollector, e);
  288. }
  289. finally {
  290. this._eventDispatcher.endEmitViewEvents();
  291. }
  292. }));
  293. this._register(this.model.onDidChangeTokens((e) => {
  294. let viewRanges = [];
  295. for (let j = 0, lenJ = e.ranges.length; j < lenJ; j++) {
  296. const modelRange = e.ranges[j];
  297. const viewStartLineNumber = this.coordinatesConverter.convertModelPositionToViewPosition(new Position(modelRange.fromLineNumber, 1)).lineNumber;
  298. const viewEndLineNumber = this.coordinatesConverter.convertModelPositionToViewPosition(new Position(modelRange.toLineNumber, this.model.getLineMaxColumn(modelRange.toLineNumber))).lineNumber;
  299. viewRanges[j] = {
  300. fromLineNumber: viewStartLineNumber,
  301. toLineNumber: viewEndLineNumber
  302. };
  303. }
  304. this._eventDispatcher.emitSingleViewEvent(new viewEvents.ViewTokensChangedEvent(viewRanges));
  305. if (e.tokenizationSupportChanged) {
  306. this._tokenizeViewportSoon.schedule();
  307. }
  308. }));
  309. this._register(this.model.onDidChangeLanguageConfiguration((e) => {
  310. this._eventDispatcher.emitSingleViewEvent(new viewEvents.ViewLanguageConfigurationEvent());
  311. this.cursorConfig = new CursorConfiguration(this.model.getLanguageId(), this.model.getOptions(), this._configuration);
  312. this._cursor.updateConfiguration(this.cursorConfig);
  313. }));
  314. this._register(this.model.onDidChangeLanguage((e) => {
  315. this.cursorConfig = new CursorConfiguration(this.model.getLanguageId(), this.model.getOptions(), this._configuration);
  316. this._cursor.updateConfiguration(this.cursorConfig);
  317. }));
  318. this._register(this.model.onDidChangeOptions((e) => {
  319. // A tab size change causes a line mapping changed event => all view parts will repaint OK, no further event needed here
  320. if (this._lines.setTabSize(this.model.getOptions().tabSize)) {
  321. try {
  322. const eventsCollector = this._eventDispatcher.beginEmitViewEvents();
  323. eventsCollector.emitViewEvent(new viewEvents.ViewFlushedEvent());
  324. eventsCollector.emitViewEvent(new viewEvents.ViewLineMappingChangedEvent());
  325. eventsCollector.emitViewEvent(new viewEvents.ViewDecorationsChangedEvent(null));
  326. this._cursor.onLineMappingChanged(eventsCollector);
  327. this._decorations.onLineMappingChanged();
  328. this.viewLayout.onFlushed(this.getLineCount());
  329. }
  330. finally {
  331. this._eventDispatcher.endEmitViewEvents();
  332. }
  333. this._updateConfigurationViewLineCount.schedule();
  334. }
  335. this.cursorConfig = new CursorConfiguration(this.model.getLanguageId(), this.model.getOptions(), this._configuration);
  336. this._cursor.updateConfiguration(this.cursorConfig);
  337. }));
  338. this._register(this.model.onDidChangeDecorations((e) => {
  339. this._decorations.onModelDecorationsChanged();
  340. this._eventDispatcher.emitSingleViewEvent(new viewEvents.ViewDecorationsChangedEvent(e));
  341. }));
  342. }
  343. setHiddenAreas(ranges) {
  344. let lineMappingChanged = false;
  345. try {
  346. const eventsCollector = this._eventDispatcher.beginEmitViewEvents();
  347. lineMappingChanged = this._lines.setHiddenAreas(ranges);
  348. if (lineMappingChanged) {
  349. eventsCollector.emitViewEvent(new viewEvents.ViewFlushedEvent());
  350. eventsCollector.emitViewEvent(new viewEvents.ViewLineMappingChangedEvent());
  351. eventsCollector.emitViewEvent(new viewEvents.ViewDecorationsChangedEvent(null));
  352. this._cursor.onLineMappingChanged(eventsCollector);
  353. this._decorations.onLineMappingChanged();
  354. this.viewLayout.onFlushed(this.getLineCount());
  355. this.viewLayout.onHeightMaybeChanged();
  356. }
  357. }
  358. finally {
  359. this._eventDispatcher.endEmitViewEvents();
  360. }
  361. this._updateConfigurationViewLineCount.schedule();
  362. if (lineMappingChanged) {
  363. this._eventDispatcher.emitOutgoingEvent(new ViewZonesChangedEvent());
  364. }
  365. }
  366. getVisibleRangesPlusViewportAboveBelow() {
  367. const layoutInfo = this._configuration.options.get(130 /* layoutInfo */);
  368. const lineHeight = this._configuration.options.get(58 /* lineHeight */);
  369. const linesAround = Math.max(20, Math.round(layoutInfo.height / lineHeight));
  370. const partialData = this.viewLayout.getLinesViewportData();
  371. const startViewLineNumber = Math.max(1, partialData.completelyVisibleStartLineNumber - linesAround);
  372. const endViewLineNumber = Math.min(this.getLineCount(), partialData.completelyVisibleEndLineNumber + linesAround);
  373. return this._toModelVisibleRanges(new Range(startViewLineNumber, this.getLineMinColumn(startViewLineNumber), endViewLineNumber, this.getLineMaxColumn(endViewLineNumber)));
  374. }
  375. getVisibleRanges() {
  376. const visibleViewRange = this.getCompletelyVisibleViewRange();
  377. return this._toModelVisibleRanges(visibleViewRange);
  378. }
  379. _toModelVisibleRanges(visibleViewRange) {
  380. const visibleRange = this.coordinatesConverter.convertViewRangeToModelRange(visibleViewRange);
  381. const hiddenAreas = this._lines.getHiddenAreas();
  382. if (hiddenAreas.length === 0) {
  383. return [visibleRange];
  384. }
  385. let result = [], resultLen = 0;
  386. let startLineNumber = visibleRange.startLineNumber;
  387. let startColumn = visibleRange.startColumn;
  388. let endLineNumber = visibleRange.endLineNumber;
  389. let endColumn = visibleRange.endColumn;
  390. for (let i = 0, len = hiddenAreas.length; i < len; i++) {
  391. const hiddenStartLineNumber = hiddenAreas[i].startLineNumber;
  392. const hiddenEndLineNumber = hiddenAreas[i].endLineNumber;
  393. if (hiddenEndLineNumber < startLineNumber) {
  394. continue;
  395. }
  396. if (hiddenStartLineNumber > endLineNumber) {
  397. continue;
  398. }
  399. if (startLineNumber < hiddenStartLineNumber) {
  400. result[resultLen++] = new Range(startLineNumber, startColumn, hiddenStartLineNumber - 1, this.model.getLineMaxColumn(hiddenStartLineNumber - 1));
  401. }
  402. startLineNumber = hiddenEndLineNumber + 1;
  403. startColumn = 1;
  404. }
  405. if (startLineNumber < endLineNumber || (startLineNumber === endLineNumber && startColumn < endColumn)) {
  406. result[resultLen++] = new Range(startLineNumber, startColumn, endLineNumber, endColumn);
  407. }
  408. return result;
  409. }
  410. getCompletelyVisibleViewRange() {
  411. const partialData = this.viewLayout.getLinesViewportData();
  412. const startViewLineNumber = partialData.completelyVisibleStartLineNumber;
  413. const endViewLineNumber = partialData.completelyVisibleEndLineNumber;
  414. return new Range(startViewLineNumber, this.getLineMinColumn(startViewLineNumber), endViewLineNumber, this.getLineMaxColumn(endViewLineNumber));
  415. }
  416. getCompletelyVisibleViewRangeAtScrollTop(scrollTop) {
  417. const partialData = this.viewLayout.getLinesViewportDataAtScrollTop(scrollTop);
  418. const startViewLineNumber = partialData.completelyVisibleStartLineNumber;
  419. const endViewLineNumber = partialData.completelyVisibleEndLineNumber;
  420. return new Range(startViewLineNumber, this.getLineMinColumn(startViewLineNumber), endViewLineNumber, this.getLineMaxColumn(endViewLineNumber));
  421. }
  422. saveState() {
  423. const compatViewState = this.viewLayout.saveState();
  424. const scrollTop = compatViewState.scrollTop;
  425. const firstViewLineNumber = this.viewLayout.getLineNumberAtVerticalOffset(scrollTop);
  426. const firstPosition = this.coordinatesConverter.convertViewPositionToModelPosition(new Position(firstViewLineNumber, this.getLineMinColumn(firstViewLineNumber)));
  427. const firstPositionDeltaTop = this.viewLayout.getVerticalOffsetForLineNumber(firstViewLineNumber) - scrollTop;
  428. return {
  429. scrollLeft: compatViewState.scrollLeft,
  430. firstPosition: firstPosition,
  431. firstPositionDeltaTop: firstPositionDeltaTop
  432. };
  433. }
  434. reduceRestoreState(state) {
  435. if (typeof state.firstPosition === 'undefined') {
  436. // This is a view state serialized by an older version
  437. return this._reduceRestoreStateCompatibility(state);
  438. }
  439. const modelPosition = this.model.validatePosition(state.firstPosition);
  440. const viewPosition = this.coordinatesConverter.convertModelPositionToViewPosition(modelPosition);
  441. const scrollTop = this.viewLayout.getVerticalOffsetForLineNumber(viewPosition.lineNumber) - state.firstPositionDeltaTop;
  442. return {
  443. scrollLeft: state.scrollLeft,
  444. scrollTop: scrollTop
  445. };
  446. }
  447. _reduceRestoreStateCompatibility(state) {
  448. return {
  449. scrollLeft: state.scrollLeft,
  450. scrollTop: state.scrollTopWithoutViewZones
  451. };
  452. }
  453. getTabSize() {
  454. return this.model.getOptions().tabSize;
  455. }
  456. getTextModelOptions() {
  457. return this.model.getOptions();
  458. }
  459. getLineCount() {
  460. return this._lines.getViewLineCount();
  461. }
  462. /**
  463. * Gives a hint that a lot of requests are about to come in for these line numbers.
  464. */
  465. setViewport(startLineNumber, endLineNumber, centeredLineNumber) {
  466. this._viewportStartLine = startLineNumber;
  467. let position = this.coordinatesConverter.convertViewPositionToModelPosition(new Position(startLineNumber, this.getLineMinColumn(startLineNumber)));
  468. this._viewportStartLineTrackedRange = this.model._setTrackedRange(this._viewportStartLineTrackedRange, new Range(position.lineNumber, position.column, position.lineNumber, position.column), 1 /* NeverGrowsWhenTypingAtEdges */);
  469. const viewportStartLineTop = this.viewLayout.getVerticalOffsetForLineNumber(startLineNumber);
  470. const scrollTop = this.viewLayout.getCurrentScrollTop();
  471. this._viewportStartLineDelta = scrollTop - viewportStartLineTop;
  472. }
  473. getActiveIndentGuide(lineNumber, minLineNumber, maxLineNumber) {
  474. return this._lines.getActiveIndentGuide(lineNumber, minLineNumber, maxLineNumber);
  475. }
  476. getLinesIndentGuides(startLineNumber, endLineNumber) {
  477. return this._lines.getViewLinesIndentGuides(startLineNumber, endLineNumber);
  478. }
  479. getBracketGuidesInRangeByLine(startLineNumber, endLineNumber, activePosition, options) {
  480. return this._lines.getViewLinesBracketGuides(startLineNumber, endLineNumber, activePosition, options);
  481. }
  482. getLineContent(lineNumber) {
  483. return this._lines.getViewLineContent(lineNumber);
  484. }
  485. getLineLength(lineNumber) {
  486. return this._lines.getViewLineLength(lineNumber);
  487. }
  488. getLineMinColumn(lineNumber) {
  489. return this._lines.getViewLineMinColumn(lineNumber);
  490. }
  491. getLineMaxColumn(lineNumber) {
  492. return this._lines.getViewLineMaxColumn(lineNumber);
  493. }
  494. getLineFirstNonWhitespaceColumn(lineNumber) {
  495. const result = strings.firstNonWhitespaceIndex(this.getLineContent(lineNumber));
  496. if (result === -1) {
  497. return 0;
  498. }
  499. return result + 1;
  500. }
  501. getLineLastNonWhitespaceColumn(lineNumber) {
  502. const result = strings.lastNonWhitespaceIndex(this.getLineContent(lineNumber));
  503. if (result === -1) {
  504. return 0;
  505. }
  506. return result + 2;
  507. }
  508. getDecorationsInViewport(visibleRange) {
  509. return this._decorations.getDecorationsViewportData(visibleRange).decorations;
  510. }
  511. getInjectedTextAt(viewPosition) {
  512. return this._lines.getInjectedTextAt(viewPosition);
  513. }
  514. getViewLineRenderingData(visibleRange, lineNumber) {
  515. let mightContainRTL = this.model.mightContainRTL();
  516. let mightContainNonBasicASCII = this.model.mightContainNonBasicASCII();
  517. let tabSize = this.getTabSize();
  518. let lineData = this._lines.getViewLineData(lineNumber);
  519. let allInlineDecorations = this._decorations.getDecorationsViewportData(visibleRange).inlineDecorations;
  520. let inlineDecorations = allInlineDecorations[lineNumber - visibleRange.startLineNumber];
  521. if (lineData.inlineDecorations) {
  522. inlineDecorations = [
  523. ...inlineDecorations,
  524. ...lineData.inlineDecorations.map(d => d.toInlineDecoration(lineNumber))
  525. ];
  526. }
  527. return new ViewLineRenderingData(lineData.minColumn, lineData.maxColumn, lineData.content, lineData.continuesWithWrappedLine, mightContainRTL, mightContainNonBasicASCII, lineData.tokens, inlineDecorations, tabSize, lineData.startVisibleColumn);
  528. }
  529. getViewLineData(lineNumber) {
  530. return this._lines.getViewLineData(lineNumber);
  531. }
  532. getMinimapLinesRenderingData(startLineNumber, endLineNumber, needed) {
  533. let result = this._lines.getViewLinesData(startLineNumber, endLineNumber, needed);
  534. return new MinimapLinesRenderingData(this.getTabSize(), result);
  535. }
  536. getAllOverviewRulerDecorations(theme) {
  537. const decorations = this.model.getOverviewRulerDecorations(this._editorId, filterValidationDecorations(this._configuration.options));
  538. const result = new OverviewRulerDecorations();
  539. for (const decoration of decorations) {
  540. const decorationOptions = decoration.options;
  541. const opts = decorationOptions.overviewRuler;
  542. if (!opts) {
  543. continue;
  544. }
  545. const lane = opts.position;
  546. if (lane === 0) {
  547. continue;
  548. }
  549. const color = opts.getColor(theme);
  550. const viewStartLineNumber = this.coordinatesConverter.getViewLineNumberOfModelPosition(decoration.range.startLineNumber, decoration.range.startColumn);
  551. const viewEndLineNumber = this.coordinatesConverter.getViewLineNumberOfModelPosition(decoration.range.endLineNumber, decoration.range.endColumn);
  552. result.accept(color, decorationOptions.zIndex, viewStartLineNumber, viewEndLineNumber, lane);
  553. }
  554. return result.asArray;
  555. }
  556. invalidateOverviewRulerColorCache() {
  557. const decorations = this.model.getOverviewRulerDecorations();
  558. for (const decoration of decorations) {
  559. const opts = decoration.options.overviewRuler;
  560. if (opts) {
  561. opts.invalidateCachedColor();
  562. }
  563. }
  564. }
  565. invalidateMinimapColorCache() {
  566. const decorations = this.model.getAllDecorations();
  567. for (const decoration of decorations) {
  568. const opts = decoration.options.minimap;
  569. if (opts) {
  570. opts.invalidateCachedColor();
  571. }
  572. }
  573. }
  574. getValueInRange(range, eol) {
  575. const modelRange = this.coordinatesConverter.convertViewRangeToModelRange(range);
  576. return this.model.getValueInRange(modelRange, eol);
  577. }
  578. getModelLineMaxColumn(modelLineNumber) {
  579. return this.model.getLineMaxColumn(modelLineNumber);
  580. }
  581. validateModelPosition(position) {
  582. return this.model.validatePosition(position);
  583. }
  584. validateModelRange(range) {
  585. return this.model.validateRange(range);
  586. }
  587. deduceModelPositionRelativeToViewPosition(viewAnchorPosition, deltaOffset, lineFeedCnt) {
  588. const modelAnchor = this.coordinatesConverter.convertViewPositionToModelPosition(viewAnchorPosition);
  589. if (this.model.getEOL().length === 2) {
  590. // This model uses CRLF, so the delta must take that into account
  591. if (deltaOffset < 0) {
  592. deltaOffset -= lineFeedCnt;
  593. }
  594. else {
  595. deltaOffset += lineFeedCnt;
  596. }
  597. }
  598. const modelAnchorOffset = this.model.getOffsetAt(modelAnchor);
  599. const resultOffset = modelAnchorOffset + deltaOffset;
  600. return this.model.getPositionAt(resultOffset);
  601. }
  602. getEOL() {
  603. return this.model.getEOL();
  604. }
  605. getPlainTextToCopy(modelRanges, emptySelectionClipboard, forceCRLF) {
  606. const newLineCharacter = forceCRLF ? '\r\n' : this.model.getEOL();
  607. modelRanges = modelRanges.slice(0);
  608. modelRanges.sort(Range.compareRangesUsingStarts);
  609. let hasEmptyRange = false;
  610. let hasNonEmptyRange = false;
  611. for (const range of modelRanges) {
  612. if (range.isEmpty()) {
  613. hasEmptyRange = true;
  614. }
  615. else {
  616. hasNonEmptyRange = true;
  617. }
  618. }
  619. if (!hasNonEmptyRange) {
  620. // all ranges are empty
  621. if (!emptySelectionClipboard) {
  622. return '';
  623. }
  624. const modelLineNumbers = modelRanges.map((r) => r.startLineNumber);
  625. let result = '';
  626. for (let i = 0; i < modelLineNumbers.length; i++) {
  627. if (i > 0 && modelLineNumbers[i - 1] === modelLineNumbers[i]) {
  628. continue;
  629. }
  630. result += this.model.getLineContent(modelLineNumbers[i]) + newLineCharacter;
  631. }
  632. return result;
  633. }
  634. if (hasEmptyRange && emptySelectionClipboard) {
  635. // mixed empty selections and non-empty selections
  636. let result = [];
  637. let prevModelLineNumber = 0;
  638. for (const modelRange of modelRanges) {
  639. const modelLineNumber = modelRange.startLineNumber;
  640. if (modelRange.isEmpty()) {
  641. if (modelLineNumber !== prevModelLineNumber) {
  642. result.push(this.model.getLineContent(modelLineNumber));
  643. }
  644. }
  645. else {
  646. result.push(this.model.getValueInRange(modelRange, forceCRLF ? 2 /* CRLF */ : 0 /* TextDefined */));
  647. }
  648. prevModelLineNumber = modelLineNumber;
  649. }
  650. return result.length === 1 ? result[0] : result;
  651. }
  652. let result = [];
  653. for (const modelRange of modelRanges) {
  654. if (!modelRange.isEmpty()) {
  655. result.push(this.model.getValueInRange(modelRange, forceCRLF ? 2 /* CRLF */ : 0 /* TextDefined */));
  656. }
  657. }
  658. return result.length === 1 ? result[0] : result;
  659. }
  660. getRichTextToCopy(modelRanges, emptySelectionClipboard) {
  661. const languageId = this.model.getLanguageId();
  662. if (languageId === PLAINTEXT_MODE_ID) {
  663. return null;
  664. }
  665. if (modelRanges.length !== 1) {
  666. // no multiple selection support at this time
  667. return null;
  668. }
  669. let range = modelRanges[0];
  670. if (range.isEmpty()) {
  671. if (!emptySelectionClipboard) {
  672. // nothing to copy
  673. return null;
  674. }
  675. const lineNumber = range.startLineNumber;
  676. range = new Range(lineNumber, this.model.getLineMinColumn(lineNumber), lineNumber, this.model.getLineMaxColumn(lineNumber));
  677. }
  678. const fontInfo = this._configuration.options.get(43 /* fontInfo */);
  679. const colorMap = this._getColorMap();
  680. const hasBadChars = (/[:;\\\/<>]/.test(fontInfo.fontFamily));
  681. const useDefaultFontFamily = (hasBadChars || fontInfo.fontFamily === EDITOR_FONT_DEFAULTS.fontFamily);
  682. let fontFamily;
  683. if (useDefaultFontFamily) {
  684. fontFamily = EDITOR_FONT_DEFAULTS.fontFamily;
  685. }
  686. else {
  687. fontFamily = fontInfo.fontFamily;
  688. fontFamily = fontFamily.replace(/"/g, '\'');
  689. const hasQuotesOrIsList = /[,']/.test(fontFamily);
  690. if (!hasQuotesOrIsList) {
  691. const needsQuotes = /[+ ]/.test(fontFamily);
  692. if (needsQuotes) {
  693. fontFamily = `'${fontFamily}'`;
  694. }
  695. }
  696. fontFamily = `${fontFamily}, ${EDITOR_FONT_DEFAULTS.fontFamily}`;
  697. }
  698. return {
  699. mode: languageId,
  700. html: (`<div style="`
  701. + `color: ${colorMap[1 /* DefaultForeground */]};`
  702. + `background-color: ${colorMap[2 /* DefaultBackground */]};`
  703. + `font-family: ${fontFamily};`
  704. + `font-weight: ${fontInfo.fontWeight};`
  705. + `font-size: ${fontInfo.fontSize}px;`
  706. + `line-height: ${fontInfo.lineHeight}px;`
  707. + `white-space: pre;`
  708. + `">`
  709. + this._getHTMLToCopy(range, colorMap)
  710. + '</div>')
  711. };
  712. }
  713. _getHTMLToCopy(modelRange, colorMap) {
  714. const startLineNumber = modelRange.startLineNumber;
  715. const startColumn = modelRange.startColumn;
  716. const endLineNumber = modelRange.endLineNumber;
  717. const endColumn = modelRange.endColumn;
  718. const tabSize = this.getTabSize();
  719. let result = '';
  720. for (let lineNumber = startLineNumber; lineNumber <= endLineNumber; lineNumber++) {
  721. const lineTokens = this.model.getLineTokens(lineNumber);
  722. const lineContent = lineTokens.getLineContent();
  723. const startOffset = (lineNumber === startLineNumber ? startColumn - 1 : 0);
  724. const endOffset = (lineNumber === endLineNumber ? endColumn - 1 : lineContent.length);
  725. if (lineContent === '') {
  726. result += '<br>';
  727. }
  728. else {
  729. result += tokenizeLineToHTML(lineContent, lineTokens.inflate(), colorMap, startOffset, endOffset, tabSize, platform.isWindows);
  730. }
  731. }
  732. return result;
  733. }
  734. _getColorMap() {
  735. let colorMap = TokenizationRegistry.getColorMap();
  736. let result = ['#000000'];
  737. if (colorMap) {
  738. for (let i = 1, len = colorMap.length; i < len; i++) {
  739. result[i] = Color.Format.CSS.formatHex(colorMap[i]);
  740. }
  741. }
  742. return result;
  743. }
  744. //#region model
  745. pushStackElement() {
  746. this.model.pushStackElement();
  747. }
  748. //#endregion
  749. //#region cursor operations
  750. getPrimaryCursorState() {
  751. return this._cursor.getPrimaryCursorState();
  752. }
  753. getLastAddedCursorIndex() {
  754. return this._cursor.getLastAddedCursorIndex();
  755. }
  756. getCursorStates() {
  757. return this._cursor.getCursorStates();
  758. }
  759. setCursorStates(source, reason, states) {
  760. this._withViewEventsCollector(eventsCollector => this._cursor.setStates(eventsCollector, source, reason, states));
  761. }
  762. getCursorColumnSelectData() {
  763. return this._cursor.getCursorColumnSelectData();
  764. }
  765. getCursorAutoClosedCharacters() {
  766. return this._cursor.getAutoClosedCharacters();
  767. }
  768. setCursorColumnSelectData(columnSelectData) {
  769. this._cursor.setCursorColumnSelectData(columnSelectData);
  770. }
  771. getPrevEditOperationType() {
  772. return this._cursor.getPrevEditOperationType();
  773. }
  774. setPrevEditOperationType(type) {
  775. this._cursor.setPrevEditOperationType(type);
  776. }
  777. getSelection() {
  778. return this._cursor.getSelection();
  779. }
  780. getSelections() {
  781. return this._cursor.getSelections();
  782. }
  783. getPosition() {
  784. return this._cursor.getPrimaryCursorState().modelState.position;
  785. }
  786. setSelections(source, selections, reason = 0 /* NotSet */) {
  787. this._withViewEventsCollector(eventsCollector => this._cursor.setSelections(eventsCollector, source, selections, reason));
  788. }
  789. saveCursorState() {
  790. return this._cursor.saveState();
  791. }
  792. restoreCursorState(states) {
  793. this._withViewEventsCollector(eventsCollector => this._cursor.restoreState(eventsCollector, states));
  794. }
  795. _executeCursorEdit(callback) {
  796. if (this._cursor.context.cursorConfig.readOnly) {
  797. // we cannot edit when read only...
  798. this._eventDispatcher.emitOutgoingEvent(new ReadOnlyEditAttemptEvent());
  799. return;
  800. }
  801. this._withViewEventsCollector(callback);
  802. }
  803. executeEdits(source, edits, cursorStateComputer) {
  804. this._executeCursorEdit(eventsCollector => this._cursor.executeEdits(eventsCollector, source, edits, cursorStateComputer));
  805. }
  806. startComposition() {
  807. this._cursor.setIsDoingComposition(true);
  808. this._executeCursorEdit(eventsCollector => this._cursor.startComposition(eventsCollector));
  809. }
  810. endComposition(source) {
  811. this._cursor.setIsDoingComposition(false);
  812. this._executeCursorEdit(eventsCollector => this._cursor.endComposition(eventsCollector, source));
  813. }
  814. type(text, source) {
  815. this._executeCursorEdit(eventsCollector => this._cursor.type(eventsCollector, text, source));
  816. }
  817. compositionType(text, replacePrevCharCnt, replaceNextCharCnt, positionDelta, source) {
  818. this._executeCursorEdit(eventsCollector => this._cursor.compositionType(eventsCollector, text, replacePrevCharCnt, replaceNextCharCnt, positionDelta, source));
  819. }
  820. paste(text, pasteOnNewLine, multicursorText, source) {
  821. this._executeCursorEdit(eventsCollector => this._cursor.paste(eventsCollector, text, pasteOnNewLine, multicursorText, source));
  822. }
  823. cut(source) {
  824. this._executeCursorEdit(eventsCollector => this._cursor.cut(eventsCollector, source));
  825. }
  826. executeCommand(command, source) {
  827. this._executeCursorEdit(eventsCollector => this._cursor.executeCommand(eventsCollector, command, source));
  828. }
  829. executeCommands(commands, source) {
  830. this._executeCursorEdit(eventsCollector => this._cursor.executeCommands(eventsCollector, commands, source));
  831. }
  832. revealPrimaryCursor(source, revealHorizontal) {
  833. this._withViewEventsCollector(eventsCollector => this._cursor.revealPrimary(eventsCollector, source, revealHorizontal, 0 /* Smooth */));
  834. }
  835. revealTopMostCursor(source) {
  836. const viewPosition = this._cursor.getTopMostViewPosition();
  837. const viewRange = new Range(viewPosition.lineNumber, viewPosition.column, viewPosition.lineNumber, viewPosition.column);
  838. this._withViewEventsCollector(eventsCollector => eventsCollector.emitViewEvent(new viewEvents.ViewRevealRangeRequestEvent(source, viewRange, null, 0 /* Simple */, true, 0 /* Smooth */)));
  839. }
  840. revealBottomMostCursor(source) {
  841. const viewPosition = this._cursor.getBottomMostViewPosition();
  842. const viewRange = new Range(viewPosition.lineNumber, viewPosition.column, viewPosition.lineNumber, viewPosition.column);
  843. this._withViewEventsCollector(eventsCollector => eventsCollector.emitViewEvent(new viewEvents.ViewRevealRangeRequestEvent(source, viewRange, null, 0 /* Simple */, true, 0 /* Smooth */)));
  844. }
  845. revealRange(source, revealHorizontal, viewRange, verticalType, scrollType) {
  846. this._withViewEventsCollector(eventsCollector => eventsCollector.emitViewEvent(new viewEvents.ViewRevealRangeRequestEvent(source, viewRange, null, verticalType, revealHorizontal, scrollType)));
  847. }
  848. //#endregion
  849. //#region viewLayout
  850. getVerticalOffsetForLineNumber(viewLineNumber) {
  851. return this.viewLayout.getVerticalOffsetForLineNumber(viewLineNumber);
  852. }
  853. getScrollTop() {
  854. return this.viewLayout.getCurrentScrollTop();
  855. }
  856. setScrollTop(newScrollTop, scrollType) {
  857. this.viewLayout.setScrollPosition({ scrollTop: newScrollTop }, scrollType);
  858. }
  859. setScrollPosition(position, type) {
  860. this.viewLayout.setScrollPosition(position, type);
  861. }
  862. deltaScrollNow(deltaScrollLeft, deltaScrollTop) {
  863. this.viewLayout.deltaScrollNow(deltaScrollLeft, deltaScrollTop);
  864. }
  865. changeWhitespace(callback) {
  866. const hadAChange = this.viewLayout.changeWhitespace(callback);
  867. if (hadAChange) {
  868. this._eventDispatcher.emitSingleViewEvent(new viewEvents.ViewZonesChangedEvent());
  869. this._eventDispatcher.emitOutgoingEvent(new ViewZonesChangedEvent());
  870. }
  871. }
  872. setMaxLineWidth(maxLineWidth) {
  873. this.viewLayout.setMaxLineWidth(maxLineWidth);
  874. }
  875. //#endregion
  876. _withViewEventsCollector(callback) {
  877. try {
  878. const eventsCollector = this._eventDispatcher.beginEmitViewEvents();
  879. callback(eventsCollector);
  880. }
  881. finally {
  882. this._eventDispatcher.endEmitViewEvents();
  883. }
  884. }
  885. normalizePosition(position, affinity) {
  886. return this._lines.normalizePosition(position, affinity);
  887. }
  888. /**
  889. * Gets the column at which indentation stops at a given line.
  890. * @internal
  891. */
  892. getLineIndentColumn(lineNumber) {
  893. return this._lines.getLineIndentColumn(lineNumber);
  894. }
  895. }
  896. class OverviewRulerDecorations {
  897. constructor() {
  898. this._asMap = Object.create(null);
  899. this.asArray = [];
  900. }
  901. accept(color, zIndex, startLineNumber, endLineNumber, lane) {
  902. const prevGroup = this._asMap[color];
  903. if (prevGroup) {
  904. const prevData = prevGroup.data;
  905. const prevLane = prevData[prevData.length - 3];
  906. const prevEndLineNumber = prevData[prevData.length - 1];
  907. if (prevLane === lane && prevEndLineNumber + 1 >= startLineNumber) {
  908. // merge into prev
  909. if (endLineNumber > prevEndLineNumber) {
  910. prevData[prevData.length - 1] = endLineNumber;
  911. }
  912. return;
  913. }
  914. // push
  915. prevData.push(lane, startLineNumber, endLineNumber);
  916. }
  917. else {
  918. const group = new OverviewRulerDecorationsGroup(color, zIndex, [lane, startLineNumber, endLineNumber]);
  919. this._asMap[color] = group;
  920. this.asArray.push(group);
  921. }
  922. }
  923. }