71db5b024a84d92ce69e3468aa500f1b4939d8628a0c6a6ebc1d19f579ce66b2bd57cd76c54092a5fe9c1ca3ce756106bfd4643d48a26b78c5f872201b99d7 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329
  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 { LineTokens } from '../tokens/lineTokens.js';
  6. import { Position } from '../core/position.js';
  7. import { LineInjectedText } from '../textModelEvents.js';
  8. import { SingleLineInlineDecoration, ViewLineData } from '../viewModel.js';
  9. export function createModelLineProjection(lineBreakData, isVisible) {
  10. if (lineBreakData === null) {
  11. // No mapping needed
  12. if (isVisible) {
  13. return IdentityModelLineProjection.INSTANCE;
  14. }
  15. return HiddenModelLineProjection.INSTANCE;
  16. }
  17. else {
  18. return new ModelLineProjection(lineBreakData, isVisible);
  19. }
  20. }
  21. /**
  22. * This projection is used to
  23. * * wrap model lines
  24. * * inject text
  25. */
  26. class ModelLineProjection {
  27. constructor(lineBreakData, isVisible) {
  28. this._projectionData = lineBreakData;
  29. this._isVisible = isVisible;
  30. }
  31. isVisible() {
  32. return this._isVisible;
  33. }
  34. setVisible(isVisible) {
  35. this._isVisible = isVisible;
  36. return this;
  37. }
  38. getProjectionData() {
  39. return this._projectionData;
  40. }
  41. getViewLineCount() {
  42. if (!this._isVisible) {
  43. return 0;
  44. }
  45. return this._projectionData.getOutputLineCount();
  46. }
  47. getViewLineContent(model, modelLineNumber, outputLineIndex) {
  48. this._assertVisible();
  49. const startOffsetInInputWithInjections = outputLineIndex > 0 ? this._projectionData.breakOffsets[outputLineIndex - 1] : 0;
  50. const endOffsetInInputWithInjections = this._projectionData.breakOffsets[outputLineIndex];
  51. let r;
  52. if (this._projectionData.injectionOffsets !== null) {
  53. const injectedTexts = this._projectionData.injectionOffsets.map((offset, idx) => new LineInjectedText(0, 0, offset + 1, this._projectionData.injectionOptions[idx], 0));
  54. const lineWithInjections = LineInjectedText.applyInjectedText(model.getLineContent(modelLineNumber), injectedTexts);
  55. r = lineWithInjections.substring(startOffsetInInputWithInjections, endOffsetInInputWithInjections);
  56. }
  57. else {
  58. r = model.getValueInRange({
  59. startLineNumber: modelLineNumber,
  60. startColumn: startOffsetInInputWithInjections + 1,
  61. endLineNumber: modelLineNumber,
  62. endColumn: endOffsetInInputWithInjections + 1
  63. });
  64. }
  65. if (outputLineIndex > 0) {
  66. r = spaces(this._projectionData.wrappedTextIndentLength) + r;
  67. }
  68. return r;
  69. }
  70. getViewLineLength(model, modelLineNumber, outputLineIndex) {
  71. this._assertVisible();
  72. return this._projectionData.getLineLength(outputLineIndex);
  73. }
  74. getViewLineMinColumn(_model, _modelLineNumber, outputLineIndex) {
  75. this._assertVisible();
  76. return this._projectionData.getMinOutputOffset(outputLineIndex) + 1;
  77. }
  78. getViewLineMaxColumn(model, modelLineNumber, outputLineIndex) {
  79. this._assertVisible();
  80. return this._projectionData.getMaxOutputOffset(outputLineIndex) + 1;
  81. }
  82. /**
  83. * Try using {@link getViewLinesData} instead.
  84. */
  85. getViewLineData(model, modelLineNumber, outputLineIndex) {
  86. const arr = new Array();
  87. this.getViewLinesData(model, modelLineNumber, outputLineIndex, 1, 0, [true], arr);
  88. return arr[0];
  89. }
  90. getViewLinesData(model, modelLineNumber, outputLineIdx, lineCount, globalStartIndex, needed, result) {
  91. this._assertVisible();
  92. const lineBreakData = this._projectionData;
  93. const injectionOffsets = lineBreakData.injectionOffsets;
  94. const injectionOptions = lineBreakData.injectionOptions;
  95. let inlineDecorationsPerOutputLine = null;
  96. if (injectionOffsets) {
  97. inlineDecorationsPerOutputLine = [];
  98. let totalInjectedTextLengthBefore = 0;
  99. let currentInjectedOffset = 0;
  100. for (let outputLineIndex = 0; outputLineIndex < lineBreakData.getOutputLineCount(); outputLineIndex++) {
  101. const inlineDecorations = new Array();
  102. inlineDecorationsPerOutputLine[outputLineIndex] = inlineDecorations;
  103. const lineStartOffsetInInputWithInjections = outputLineIndex > 0 ? lineBreakData.breakOffsets[outputLineIndex - 1] : 0;
  104. const lineEndOffsetInInputWithInjections = lineBreakData.breakOffsets[outputLineIndex];
  105. while (currentInjectedOffset < injectionOffsets.length) {
  106. const length = injectionOptions[currentInjectedOffset].content.length;
  107. const injectedTextStartOffsetInInputWithInjections = injectionOffsets[currentInjectedOffset] + totalInjectedTextLengthBefore;
  108. const injectedTextEndOffsetInInputWithInjections = injectedTextStartOffsetInInputWithInjections + length;
  109. if (injectedTextStartOffsetInInputWithInjections > lineEndOffsetInInputWithInjections) {
  110. // Injected text only starts in later wrapped lines.
  111. break;
  112. }
  113. if (lineStartOffsetInInputWithInjections < injectedTextEndOffsetInInputWithInjections) {
  114. // Injected text ends after or in this line (but also starts in or before this line).
  115. const options = injectionOptions[currentInjectedOffset];
  116. if (options.inlineClassName) {
  117. const offset = (outputLineIndex > 0 ? lineBreakData.wrappedTextIndentLength : 0);
  118. const start = offset + Math.max(injectedTextStartOffsetInInputWithInjections - lineStartOffsetInInputWithInjections, 0);
  119. const end = offset + Math.min(injectedTextEndOffsetInInputWithInjections - lineStartOffsetInInputWithInjections, lineEndOffsetInInputWithInjections);
  120. if (start !== end) {
  121. inlineDecorations.push(new SingleLineInlineDecoration(start, end, options.inlineClassName, options.inlineClassNameAffectsLetterSpacing));
  122. }
  123. }
  124. }
  125. if (injectedTextEndOffsetInInputWithInjections <= lineEndOffsetInInputWithInjections) {
  126. totalInjectedTextLengthBefore += length;
  127. currentInjectedOffset++;
  128. }
  129. else {
  130. // injected text breaks into next line, process it again
  131. break;
  132. }
  133. }
  134. }
  135. }
  136. let lineWithInjections;
  137. if (injectionOffsets) {
  138. lineWithInjections = model.tokenization.getLineTokens(modelLineNumber).withInserted(injectionOffsets.map((offset, idx) => ({
  139. offset,
  140. text: injectionOptions[idx].content,
  141. tokenMetadata: LineTokens.defaultTokenMetadata
  142. })));
  143. }
  144. else {
  145. lineWithInjections = model.tokenization.getLineTokens(modelLineNumber);
  146. }
  147. for (let outputLineIndex = outputLineIdx; outputLineIndex < outputLineIdx + lineCount; outputLineIndex++) {
  148. const globalIndex = globalStartIndex + outputLineIndex - outputLineIdx;
  149. if (!needed[globalIndex]) {
  150. result[globalIndex] = null;
  151. continue;
  152. }
  153. result[globalIndex] = this._getViewLineData(lineWithInjections, inlineDecorationsPerOutputLine ? inlineDecorationsPerOutputLine[outputLineIndex] : null, outputLineIndex);
  154. }
  155. }
  156. _getViewLineData(lineWithInjections, inlineDecorations, outputLineIndex) {
  157. this._assertVisible();
  158. const lineBreakData = this._projectionData;
  159. const deltaStartIndex = (outputLineIndex > 0 ? lineBreakData.wrappedTextIndentLength : 0);
  160. const lineStartOffsetInInputWithInjections = outputLineIndex > 0 ? lineBreakData.breakOffsets[outputLineIndex - 1] : 0;
  161. const lineEndOffsetInInputWithInjections = lineBreakData.breakOffsets[outputLineIndex];
  162. const tokens = lineWithInjections.sliceAndInflate(lineStartOffsetInInputWithInjections, lineEndOffsetInInputWithInjections, deltaStartIndex);
  163. let lineContent = tokens.getLineContent();
  164. if (outputLineIndex > 0) {
  165. lineContent = spaces(lineBreakData.wrappedTextIndentLength) + lineContent;
  166. }
  167. const minColumn = this._projectionData.getMinOutputOffset(outputLineIndex) + 1;
  168. const maxColumn = lineContent.length + 1;
  169. const continuesWithWrappedLine = (outputLineIndex + 1 < this.getViewLineCount());
  170. const startVisibleColumn = (outputLineIndex === 0 ? 0 : lineBreakData.breakOffsetsVisibleColumn[outputLineIndex - 1]);
  171. return new ViewLineData(lineContent, continuesWithWrappedLine, minColumn, maxColumn, startVisibleColumn, tokens, inlineDecorations);
  172. }
  173. getModelColumnOfViewPosition(outputLineIndex, outputColumn) {
  174. this._assertVisible();
  175. return this._projectionData.translateToInputOffset(outputLineIndex, outputColumn - 1) + 1;
  176. }
  177. getViewPositionOfModelPosition(deltaLineNumber, inputColumn, affinity = 2 /* PositionAffinity.None */) {
  178. this._assertVisible();
  179. const r = this._projectionData.translateToOutputPosition(inputColumn - 1, affinity);
  180. return r.toPosition(deltaLineNumber);
  181. }
  182. getViewLineNumberOfModelPosition(deltaLineNumber, inputColumn) {
  183. this._assertVisible();
  184. const r = this._projectionData.translateToOutputPosition(inputColumn - 1);
  185. return deltaLineNumber + r.outputLineIndex;
  186. }
  187. normalizePosition(outputLineIndex, outputPosition, affinity) {
  188. const baseViewLineNumber = outputPosition.lineNumber - outputLineIndex;
  189. const normalizedOutputPosition = this._projectionData.normalizeOutputPosition(outputLineIndex, outputPosition.column - 1, affinity);
  190. const result = normalizedOutputPosition.toPosition(baseViewLineNumber);
  191. return result;
  192. }
  193. getInjectedTextAt(outputLineIndex, outputColumn) {
  194. return this._projectionData.getInjectedText(outputLineIndex, outputColumn - 1);
  195. }
  196. _assertVisible() {
  197. if (!this._isVisible) {
  198. throw new Error('Not supported');
  199. }
  200. }
  201. }
  202. /**
  203. * This projection does not change the model line.
  204. */
  205. class IdentityModelLineProjection {
  206. constructor() { }
  207. isVisible() {
  208. return true;
  209. }
  210. setVisible(isVisible) {
  211. if (isVisible) {
  212. return this;
  213. }
  214. return HiddenModelLineProjection.INSTANCE;
  215. }
  216. getProjectionData() {
  217. return null;
  218. }
  219. getViewLineCount() {
  220. return 1;
  221. }
  222. getViewLineContent(model, modelLineNumber, _outputLineIndex) {
  223. return model.getLineContent(modelLineNumber);
  224. }
  225. getViewLineLength(model, modelLineNumber, _outputLineIndex) {
  226. return model.getLineLength(modelLineNumber);
  227. }
  228. getViewLineMinColumn(model, modelLineNumber, _outputLineIndex) {
  229. return model.getLineMinColumn(modelLineNumber);
  230. }
  231. getViewLineMaxColumn(model, modelLineNumber, _outputLineIndex) {
  232. return model.getLineMaxColumn(modelLineNumber);
  233. }
  234. getViewLineData(model, modelLineNumber, _outputLineIndex) {
  235. const lineTokens = model.tokenization.getLineTokens(modelLineNumber);
  236. const lineContent = lineTokens.getLineContent();
  237. return new ViewLineData(lineContent, false, 1, lineContent.length + 1, 0, lineTokens.inflate(), null);
  238. }
  239. getViewLinesData(model, modelLineNumber, _fromOuputLineIndex, _toOutputLineIndex, globalStartIndex, needed, result) {
  240. if (!needed[globalStartIndex]) {
  241. result[globalStartIndex] = null;
  242. return;
  243. }
  244. result[globalStartIndex] = this.getViewLineData(model, modelLineNumber, 0);
  245. }
  246. getModelColumnOfViewPosition(_outputLineIndex, outputColumn) {
  247. return outputColumn;
  248. }
  249. getViewPositionOfModelPosition(deltaLineNumber, inputColumn) {
  250. return new Position(deltaLineNumber, inputColumn);
  251. }
  252. getViewLineNumberOfModelPosition(deltaLineNumber, _inputColumn) {
  253. return deltaLineNumber;
  254. }
  255. normalizePosition(outputLineIndex, outputPosition, affinity) {
  256. return outputPosition;
  257. }
  258. getInjectedTextAt(_outputLineIndex, _outputColumn) {
  259. return null;
  260. }
  261. }
  262. IdentityModelLineProjection.INSTANCE = new IdentityModelLineProjection();
  263. /**
  264. * This projection hides the model line.
  265. */
  266. class HiddenModelLineProjection {
  267. constructor() { }
  268. isVisible() {
  269. return false;
  270. }
  271. setVisible(isVisible) {
  272. if (!isVisible) {
  273. return this;
  274. }
  275. return IdentityModelLineProjection.INSTANCE;
  276. }
  277. getProjectionData() {
  278. return null;
  279. }
  280. getViewLineCount() {
  281. return 0;
  282. }
  283. getViewLineContent(_model, _modelLineNumber, _outputLineIndex) {
  284. throw new Error('Not supported');
  285. }
  286. getViewLineLength(_model, _modelLineNumber, _outputLineIndex) {
  287. throw new Error('Not supported');
  288. }
  289. getViewLineMinColumn(_model, _modelLineNumber, _outputLineIndex) {
  290. throw new Error('Not supported');
  291. }
  292. getViewLineMaxColumn(_model, _modelLineNumber, _outputLineIndex) {
  293. throw new Error('Not supported');
  294. }
  295. getViewLineData(_model, _modelLineNumber, _outputLineIndex) {
  296. throw new Error('Not supported');
  297. }
  298. getViewLinesData(_model, _modelLineNumber, _fromOuputLineIndex, _toOutputLineIndex, _globalStartIndex, _needed, _result) {
  299. throw new Error('Not supported');
  300. }
  301. getModelColumnOfViewPosition(_outputLineIndex, _outputColumn) {
  302. throw new Error('Not supported');
  303. }
  304. getViewPositionOfModelPosition(_deltaLineNumber, _inputColumn) {
  305. throw new Error('Not supported');
  306. }
  307. getViewLineNumberOfModelPosition(_deltaLineNumber, _inputColumn) {
  308. throw new Error('Not supported');
  309. }
  310. normalizePosition(outputLineIndex, outputPosition, affinity) {
  311. throw new Error('Not supported');
  312. }
  313. getInjectedTextAt(_outputLineIndex, _outputColumn) {
  314. throw new Error('Not supported');
  315. }
  316. }
  317. HiddenModelLineProjection.INSTANCE = new HiddenModelLineProjection();
  318. const _spaces = [''];
  319. function spaces(count) {
  320. if (count >= _spaces.length) {
  321. for (let i = 1; i <= count; i++) {
  322. _spaces[i] = _makeSpaces(i);
  323. }
  324. }
  325. return _spaces[count];
  326. }
  327. function _makeSpaces(count) {
  328. return new Array(count + 1).join(' ');
  329. }